]> CyberLeo.Net >> Repos - SourceForge/eyefi-config.git/blob - eyefi-config.c
Add auto detection of card mount point.
[SourceForge/eyefi-config.git] / eyefi-config.c
1 /*
2  * eyefitest.c
3  *
4  * Copyright (C) 2008 Dave Hansen <dave@sr71.net>
5  *
6  * This software may be redistributed and/or modified under the terms of
7  * the GNU General Public License ("GPL") version 2 as published by the
8  * Free Software Foundation.
9  */
10
11 #include <assert.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <getopt.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <errno.h>
21
22 #define O_DIRECT        00040000        /* direct disk access hint */
23
24 enum eyefi_file {
25         REQC,
26         REQM,
27         RSPC,
28         RSPM
29 };
30  
31 #define PATHNAME_MAX 4096
32 char eyefi_mount[PATHNAME_MAX]; // PATH_MAX anyone?
33 static char *__eyefi_file(enum eyefi_file file)
34 {
35         switch (file) {
36         case REQC: return "reqc";
37         case REQM: return "reqm";
38         case RSPC: return "rspc";
39         case RSPM: return "rspm";
40         }
41
42         return NULL;
43 }
44
45 static char *eyefi_file(enum eyefi_file file)
46 {
47         char *filename = __eyefi_file(file);
48         char *full = malloc(PATHNAME_MAX);
49
50         sprintf(&full[0], "%s/EyeFi/%s", eyefi_mount, filename);
51         return full;
52 }
53
54
55 #define BUFSZ 16384
56 #define EYEFI_BUF_SIZE 16384
57 char unaligned_buf[BUFSZ*2];
58 void *buf;
59
60
61 int debug_level = 1;
62 #define debug_printf(level, args...) do {       \
63         if ((level) <= debug_level)             \
64                 fprintf(stderr, ## args);       \
65         } while(0)
66
67 /*
68  * Do some kernel-style types to make
69  * definitions shorter.
70  */
71 typedef unsigned long u32;
72 typedef unsigned char u8;
73
74 static inline u32 swap_bytes(u32 src)
75 {
76         unsigned int dest = 0;
77         dest |= (src & 0xff000000) >> 24;
78         dest |= (src & 0x00ff0000) >>  8;
79         dest |= (src & 0x0000ff00) <<  8;
80         dest |= (src & 0x000000ff) << 24;
81         return dest;
82 }
83
84 /*
85  * Just a few functions so that I can't easily forget about
86  * endinness.
87  */
88 struct __be32 {
89         u32 val;
90 } __attribute__((packed));
91 typedef struct __be32 be32;
92
93 /*
94  * These two obviously need to get fixed for
95  * big endian machines.
96  */
97 u32 be32_to_u32(be32 src)
98 {
99         return swap_bytes(src.val);
100 }
101 be32 u32_to_be32(u32 src)
102 {
103         be32 ret;
104         ret.val = swap_bytes(src);
105         return ret;
106 }
107
108 void dumpbuf(const char *buffer, int bytesToWrite)
109 {
110     int i;
111     static char linebuf[500];
112
113     for (i=0; i < bytesToWrite; i += 16) {
114         char *tmpbuf = &linebuf[0];
115         unsigned long sum = 0;
116         int j;
117 #define lprintf(args...)        do {            \
118         tmpbuf += sprintf(tmpbuf, ## args);\
119 } while (0)
120
121         lprintf("[%03d]: ", i);
122         for (j=0; j < 16; j++) {
123                 u8 c = ((unsigned char *)buffer)[i+j];
124                 lprintf("%02x ", (unsigned int)c);
125                 sum += c;
126         }
127         lprintf(" |");
128         for (j=0; j < 16; j++) {
129                 u8 c = ((unsigned char *)buffer)[i+j];
130                 if (c >= 'a' && c <= 'z')
131                         lprintf("%c", c);
132                 else if (c >= 'A' && c <= 'Z')
133                         lprintf("%c", c);
134                 else if (c >= '0' && c <= '9')
135                         lprintf("%c", c);
136                 else if (c >= 0x20 && c <= 127)
137                         lprintf("%c", c);
138                 else
139                         lprintf(".");
140         }
141         lprintf("|\n");
142         if (sum == 0)
143                 continue;
144         printf("%s", linebuf);
145         //if (i > 200)
146         //      break;
147     }
148 }
149
150 struct card_seq_num {
151         u32 seq;
152 } __attribute__((packed));
153
154 void read_from(enum eyefi_file);
155 void write_to(enum eyefi_file, void *, int);
156 struct card_seq_num read_seq_from(enum eyefi_file file)
157 {
158         struct card_seq_num *ret;
159         read_from(file);
160         ret = buf;
161         return *ret;
162 }
163
164 /*
165  * For O_DIRECT writes to files, we need
166  * to be 512 byte aligned on Linux, I think.
167  * So, just align this to something big
168  * and be done with it.  FIXME :)
169  */
170 void align_buf(void)
171 {
172         unsigned long addr = (unsigned long)&unaligned_buf[BUFSZ];
173         addr &= ~(BUFSZ-1);
174         buf = (void *)addr;
175         debug_printf(4, "buf: %p\n", buf);
176         debug_printf(4, "unaligned: %p\n", &unaligned_buf[0]);
177 }
178
179 struct card_seq_num seq;
180
181 /*
182  * The real manager does this so we might
183  * as well, too.
184  */
185 void zero_card_files(void)
186 {
187         write_to(REQM, buf, BUFSZ);
188         write_to(REQC, buf, BUFSZ);
189         write_to(RSPM, buf, BUFSZ);
190         write_to(RSPC, buf, BUFSZ);
191
192         read_from(REQM);
193         read_from(REQC);
194         read_from(RSPM);
195         read_from(RSPC);
196 }
197
198 void init_card()
199 {
200         if (buf != NULL)
201                 return;
202
203         debug_printf(2, "Initializing card...\n");
204         align_buf();
205         zero_card_files();
206         seq = read_seq_from(RSPC);
207         if (seq.seq == 0)
208                 seq.seq = 0x1234;
209         debug_printf(2, "Done initializing card...\n");
210 }
211
212 void open_error(char *file)
213 {
214         fprintf(stderr, "unable to open '%s'\n", file);
215         fprintf(stderr, "Is the Eye-Fi card inserted and mounted at: %s ?\n", eyefi_mount);
216         fprintf(stderr, "Do you have write permissions to it?\n");
217         if (debug_level > 1)
218                 perror("bad open");
219         exit(1);
220 }
221
222 void read_from(enum eyefi_file __file)
223 {
224         u8 c;
225         int i;
226         int ret, retcntl;
227         int fd;
228         int zeros = 0;
229         char *file = eyefi_file(__file);
230         
231         init_card();
232
233         fd = open(file, O_RDONLY);
234         if (fd < 0) 
235                 open_error(file);
236         retcntl = fcntl(fd, F_SETFL, O_DIRECT);
237         if (retcntl < 0) {
238                 perror("bad fcntl");
239                 exit(1);
240         }
241         ret = read(fd, buf, BUFSZ);
242         if (debug_level > 3)
243                 dumpbuf(buf, 128);
244         if (ret < 0) {
245                 perror("bad read");
246                 exit(1);
247         }
248         debug_printf(3, "read '%s': bytes: %d fcntl: %d\n", file, ret, retcntl);
249         for (i=0; i < BUFSZ; i++) {
250                 c = ((char *)buf)[i];
251                 if (c == '\0') {
252                         zeros++;
253                         continue;
254                 }
255         }
256         //if (zeros)
257         //      printf(" zeros: %d", zeros);
258         //fsync(fd);
259         free(file);
260         close(fd);
261 }
262
263 void write_to(enum eyefi_file __file, void *stuff, int len)
264 {
265         int ret;
266         int fd;
267         char *file = eyefi_file(__file);
268
269         init_card();
270         if (len == -1)
271                 len = strlen(stuff);
272
273         if (debug_level > 3) {
274                 debug_printf(3, "%s('%s', ..., %d)\n", __func__, file, len);
275                 dumpbuf(stuff, len);
276         }
277         memset(buf, 0, BUFSZ);
278         memcpy(buf, stuff, len);
279         fd = open(file, O_RDWR|O_DIRECT|O_CREAT, 0600);
280         //ret = lseek(fd, 0, SEEK_SET);
281         if (fd < 0)
282                 open_error(file);
283         if (debug_level > 3)
284                 dumpbuf(buf, 128);
285         ret = write(fd, buf, BUFSZ);
286         //fsync(fd);
287         close(fd);
288         debug_printf(3, "wrote %d bytes to '%s' (string was %d bytes)\n", ret, file, len);
289         if (ret < 0)
290                 exit(ret);
291         free(file);
292 }       
293
294 /*
295  * Most of the eyefi strings are pascal-style with
296  * a length byte preceeding content.  (Did pascal
297  * have just a byte for length or more??)
298  */
299 struct pascal_string {
300         u8 length;
301         u8 value[32];
302 } __attribute__((packed));
303
304 void print_pascal_string(struct pascal_string *str)
305 {
306         int i;
307         for (i = 0; i < str->length; i++)
308                 printf("%c", str->value[i]);
309 }
310
311 /*
312  * The 'o' command has several sub-commands:
313  */
314 enum card_info_subcommand {
315         MAC_ADDRESS   = 1,
316         FIRMWARE_INFO = 2,
317         CARD_KEY      = 3,
318         API_URL       = 4,
319         UNKNOWN1      = 5, // Chris says these are 
320         UNKNOWN2      = 6, // checksums
321         LOG_LEN       = 7,
322 };
323
324 struct card_info_req {
325         u8 o;
326         u8 subcommand;
327 } __attribute__((packed));
328
329 struct card_info_rsp_key {
330         struct pascal_string key;
331 };
332
333 #define MAC_BYTES 6
334 struct mac_address {
335         u8 length;
336         u8 mac[MAC_BYTES];
337 } __attribute__((packed));
338
339 struct card_info_api_url {
340         struct pascal_string key;
341 };
342
343 struct card_info_log_len {
344         u8 len;
345         be32 val;
346 } __attribute__((packed));
347
348 #define write_struct(file, s) write_to((file), s, sizeof(*(s)))
349
350 void print_mac(struct mac_address *mac)
351 {
352         int i;
353         for (i=0; i < MAC_BYTES-1; i++) {
354                 printf("%02x:", mac->mac[i]);
355         }
356         printf("%02x\n", mac->mac[i]);
357 }
358
359 void inc_seq(void)
360 {
361         //u32 tmpseq = be32_to_u32(seq.seq);
362         //seq.seq = u32_to_be32(tmpseq+1);
363         seq.seq++;
364         write_struct(REQC, &seq);
365 }
366
367 u32 current_seq(void)
368 {
369         return seq.seq;
370 }
371
372 void wait_for_response(void)
373 {
374         int i;
375         debug_printf(3, "waiting for response...\n");
376         inc_seq();
377         for (i = 0; i < 50; i++) {
378                 struct card_seq_num cardseq = read_seq_from(RSPC);
379                 u32 rsp = cardseq.seq;
380                 debug_printf(3, "read rsp code: %lx, looking for: %lx raw: %lx\n", rsp, current_seq(),
381                                 cardseq.seq);
382                 if (rsp == current_seq())
383                         break;
384                 usleep(300000);
385         }
386         debug_printf(3, "got good seq, reading RSPM...\n");
387         read_from(RSPM);
388         debug_printf(3, "done reading RSPM\n");
389 }
390 struct byte_response {
391         u8 response;
392 };
393
394 enum net_type {
395         UNSECURED,
396         WEP,
397         WPA,
398         WPA2
399 };
400
401 #define ESSID_LEN 32
402 struct scanned_net {
403         char essid[ESSID_LEN];
404         signed char strength;
405         u8 type;
406 } __attribute__((packed));
407
408 struct scanned_net_list {
409         u8 nr;
410         struct scanned_net nets[100];
411 } __attribute__((packed));
412
413 struct configured_net {
414         char essid[ESSID_LEN];
415 } __attribute__((packed));
416
417 struct configured_net_list {
418         u8 nr;
419         struct configured_net nets[100];
420 } __attribute__((packed));
421
422 char *net_test_states[] = {
423         "not scanning",
424         "locating network",
425         "verifying network key",
426         "waiting for DHCP",
427         "testing connection to Eye-Fi server",
428         "success",
429 };
430
431 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
432
433 char *net_test_state_name(u8 state)
434 {
435         int size = ARRAY_SIZE(net_test_states);
436         if (state >= size)
437                 return "unknown";
438         return net_test_states[state];
439 }
440
441 char *net_types[] = {
442         "No security",
443         "WEP",
444         "WPA",
445         "unknown1",
446         "WPA2",
447 };
448
449 char *net_type_name(u8 type)
450 {
451         int size = ARRAY_SIZE(net_types);
452         if (type >= size)
453                 return "unknown";
454         return net_types[type];
455 }
456
457 #define WPA_KEY_BYTES 32
458 struct wpa_key {
459         u8 key[WPA_KEY_BYTES];
460 } __attribute((packed));
461
462 #define WEP_KEY_BYTES 32
463 struct wep_key {
464         u8 key[WEP_KEY_BYTES];
465 } __attribute((packed));
466
467 struct network_key {
468         u8 len;
469         union {
470                 struct wpa_key wpa;
471                 struct wep_key wep;
472         };
473 } __attribute((packed));
474
475 #define KEY_LEN 32
476 struct net_request {
477         char req;
478         u8 essid_len;
479         char essid[ESSID_LEN];
480         struct network_key key;
481 } __attribute((packed));
482
483 char lower(char c)
484 {
485         if ((c >= 'A') && (c <= 'Z'))
486                 c += ('a' - 'A');
487         return c;
488 }
489
490 int atoh(char c)
491 {
492         char lc = lower(c);
493         if ((c >= '0') && (c <= '9'))
494                 return c - '0';
495         else if ((c >= 'a') && (c <= 'z'))
496                 return (c - 'a') + 10;
497         debug_printf(5, "non-hex character: '%c'/'%c'\n", c, lc);
498         return 0;
499 }
500
501 /*
502  * Take a string like "0ab1" and make it
503  * a series of bytes: { 0x0a, 0xb1 }
504  *
505  * Destroys the original string.
506  */
507 char *convert_ascii_to_hex(char *ascii, int len)
508 {
509         int i;
510         if (len%2) {
511                 fprintf(stderr, "%s() must be even number of bytes: %d\n",
512                 __func__, len);
513                 exit(2);
514         }
515         for (i=0; i < len; i+=2) {
516                 u8 high = atoh(ascii[i]);
517                 u8 low  = atoh(ascii[i+1]);
518                 u8 byte = (high<<4 | low);
519                 debug_printf(6, "high: %02x low: %02x, both: %02x\n", high, low, byte);
520                 ascii[i/2] = byte;
521         }
522         for (i=len/2; i < len; i++)
523                 ascii[i] = '\0';
524         return &ascii[0];
525 }
526
527 #define PASSPHRASE_PROG "wpa_passphrase"
528
529 struct wpa_key *make_wpa_key(char *essid, char *pass)
530 {
531         char program[] = PASSPHRASE_PROG;
532         // 7 for 2 spaces, 4 quotes and a \0
533         char redirect[] = " 2> /dev/null";
534         char *cmdbuf = malloc(strlen(essid) + strlen(pass) + strlen(program) + 7 + 
535                         strlen(redirect));
536
537         if (!cmdbuf)
538                 return NULL;
539
540         sprintf(cmdbuf, "%s '%s' '%s' %s", program, essid, pass, redirect);
541         FILE *pipe = popen(cmdbuf, "r");
542         if (!pipe) {
543                 perror("\nunable to execute " PASSPHRASE_PROG);
544                 return NULL;
545         }
546         
547         int key_chars = WPA_KEY_BYTES*2;
548         char hex_key_in_ascii[key_chars+1];
549         char line[1024];
550         int read = 0;
551         while (fgets(&line[0], 1023, pipe)) {
552                 debug_printf(4, "read from %s: '%s'\n", PASSPHRASE_PROG, line);
553                 read = sscanf(&line[0], "       psk=%64s", &hex_key_in_ascii[0]);
554                 if (read == 0)
555                         continue;
556                 break;
557         }
558         int exit_code = pclose(pipe);
559         if (!read || exit_code) {
560                 fprintf(stderr, "\nunable to read wpa key from %s\n", PASSPHRASE_PROG);
561                 fprintf(stderr, "Is wpa_supplicant installed?\n");
562                 exit(4);
563         }
564         debug_printf(4, "ascii key: '%s'\n", hex_key_in_ascii);
565         char *hex_key = convert_ascii_to_hex(hex_key_in_ascii, key_chars);
566         struct wpa_key *key = malloc(sizeof(*key));
567         memcpy(&key->key[0], hex_key, WPA_KEY_BYTES);
568         free(cmdbuf);
569         return key;
570 }
571
572 void card_info_cmd(enum card_info_subcommand cmd)
573 {
574         struct card_info_req cir;
575         cir.o = 'o';
576         cir.subcommand = cmd;
577
578         write_struct(REQM, &cir);
579         wait_for_response();
580 }
581
582 u32 fetch_log_length(void)
583 {
584         card_info_cmd(LOG_LEN);
585         struct card_info_log_len *loglen = buf;
586         return be32_to_u32(loglen->val);
587 }
588
589 void print_log_len(void)
590 {
591         u32 len = fetch_log_length();
592         printf("log len: %08lx\n", len);
593 }
594
595 void print_card_mac(void)
596 {
597         debug_printf(2, "%s()\n", __func__);
598         card_info_cmd(MAC_ADDRESS);
599         struct mac_address *mac = buf;
600         assert(mac->length == MAC_BYTES);
601         printf("card mac address: ");
602         print_mac(mac);
603 }
604
605 void print_card_key(void)
606 {
607         debug_printf(2, "%s()\n", __func__);
608         card_info_cmd(CARD_KEY);
609         struct card_info_rsp_key *foo = buf;
610         printf("card key (len: %d): '", foo->key.length);
611         print_pascal_string(&foo->key);
612         printf("'\n");
613 }
614
615 struct noarg_request {
616         u8 req;
617 };
618
619 void issue_noarg_command(u8 cmd)
620 {
621         struct noarg_request req;
622         req.req = cmd;
623         write_struct(REQM, &req);
624         wait_for_response();
625 }
626
627 void scan_print_nets(void)
628 {
629         int i;
630
631         debug_printf(2, "%s()\n", __func__);
632         issue_noarg_command('g');
633         struct scanned_net_list *scanned = buf;
634         if (scanned->nr == 0) {
635                 printf("unable to detect any wireless networks\n");
636                 return;
637         }
638         printf("Scanned wireless networks:\n");
639         for (i=0; i < scanned->nr; i++) {
640                 struct scanned_net *net = &scanned->nets[i];
641                 printf("'%s' type(%d): %s, strength: %d\n", net->essid,
642                                 net->type,
643                                 net_type_name(net->type),
644                                 net->strength);
645         }
646 }
647
648 void print_configured_nets(void)
649 {
650         int i;
651         struct configured_net_list *configured;
652
653         debug_printf(2, "%s()\n", __func__);
654         issue_noarg_command('l');
655         configured = buf;
656         if (configured->nr == 0) {
657                 printf("No wireless networks configured on card\n");
658                 return;
659         }
660         printf("configured wireless networks:\n");
661         for (i=0; i < configured->nr; i++) {
662                 struct configured_net *net = &configured->nets[i];
663                 printf("'%s'\n", net->essid);
664         }
665 }
666
667 void reboot_card(void)
668 {
669         debug_printf(2, "%s()\n", __func__);
670         issue_noarg_command('b');
671 }
672
673 void copy_wep_key(struct wep_key *dst, struct wep_key *src)
674 {
675         memcpy(&dst->key, &src->key, sizeof(*dst));
676 }
677
678 void copy_wpa_key(struct wpa_key *dst, struct wpa_key *src)
679 {
680         memcpy(&dst->key, &src->key, sizeof(*dst));
681 }
682
683 void network_action(char cmd, char *essid, char *wpa_ascii)
684 {
685         struct net_request nr;
686         memset(&nr, 0, sizeof(nr));
687
688         nr.req = cmd;
689         strcpy(&nr.essid[0], essid);
690         nr.essid_len = strlen(essid);
691         struct wpa_key *wpakey;
692         if (wpa_ascii) {
693                 wpakey = make_wpa_key(essid, wpa_ascii);
694                 nr.key.len = sizeof(*wpakey);
695                 copy_wpa_key(&nr.key.wpa, wpakey);
696         }
697         write_struct(REQM, &nr);
698         wait_for_response();
699 }
700
701 void add_network(char *essid, char *wpa_ascii)
702 {
703         debug_printf(2, "%s()\n", __func__);
704         network_action('a', essid, wpa_ascii);
705 }
706
707 void remove_network(char *essid)
708 {
709         debug_printf(2, "%s()\n", __func__);
710         network_action('d', essid, NULL);
711 }
712
713 int try_connection_to(char *essid, char *wpa_ascii)
714 {
715         int i;
716         int ret = -1;
717
718         char *type = net_type_name(WPA);
719         if (!wpa_ascii)
720                 type = net_type_name(UNSECURED);
721         printf("trying to connect to %s network: '%s'", type, essid);
722         if (wpa_ascii)
723                 printf(" with passphrase: '%s'", wpa_ascii);
724         fflush(NULL);
725
726         // test network
727         network_action('t', essid, wpa_ascii);
728         u8 last_rsp = -1;
729
730         char rsp = '\0';
731         for (i=0; i < 200; i++) {
732                 struct byte_response *r;
733                 issue_noarg_command('s');
734                 r = buf;
735                 rsp = r->response;
736                 char *state = net_test_state_name(rsp);
737                 if (rsp == last_rsp) {
738                         printf(".");
739                         fflush(NULL);;
740                 } else {
741                         if (rsp)
742                                 printf("\nTesting connecion to '%s' (%d): %s", essid, rsp, state);
743                         last_rsp = rsp;
744                 }
745                 
746                 if (!strcmp("success", state)) {
747                         ret = 0;
748                         break;
749                 }
750                 if (!strcmp("not scanning", state))
751                         break;
752                 if (!strcmp("unknown", state))
753                         break;
754         }
755         printf("\n");
756         if (!ret) {
757                 printf("Succeeded connecting to: '%s'\n", essid);
758         } else {
759                 printf("Unable to connect to: '%s' (final state: %d/'%s')\n", essid,
760                                 rsp, net_test_state_name(rsp));
761         }
762         return ret;
763 }
764
765 struct fetch_log_cmd {
766         char m;
767         be32 offset;
768 } __attribute__((packed));
769
770 /*
771  * When you ask for the log at offset 0x0, you
772  * get back 8 bytes of offsets into the rest of
773  * the data
774  */
775 struct first_log_response {
776         be32 log_end;
777         be32 log_start;
778         u8 data[EYEFI_BUF_SIZE-8];
779 } __attribute__((packed));
780
781 struct rest_log_response {
782         u8 data[EYEFI_BUF_SIZE];
783 } __attribute__((packed));
784
785 unsigned char *get_log_at_offset(u32 offset)
786 {
787         struct fetch_log_cmd cmd;
788         cmd.m = 'm';
789         cmd.offset = u32_to_be32(offset);
790
791         debug_printf(2, "getting log at offset: %08lx\n", offset);
792         write_struct(REQM, &cmd);
793         wait_for_response();
794         return buf;
795 }
796
797 int get_log(void)
798 {
799         int total_bytes = 0;
800         int i;
801         u32 log_start;
802         u32 log_end;
803         u32 log_size = fetch_log_length();
804         char *resbuf = malloc(log_size);
805
806         int nr_bufs_per_log = log_size/EYEFI_BUF_SIZE;
807         for (i = 0; i < log_size/EYEFI_BUF_SIZE; i++) {
808                 debug_printf(1, "fetching EyeFi card log part %d/%d...",
809                                 i+1, nr_bufs_per_log);
810                 fflush(NULL);
811                 get_log_at_offset(EYEFI_BUF_SIZE*i);
812                 debug_printf(1, "done\n");
813                 u32 log_size;
814                 u8 *log_data;
815                 if (i == 0) {
816                         struct first_log_response *log = buf;
817                         log_end = be32_to_u32(log->log_end);
818                         log_start = be32_to_u32(log->log_start);
819                         debug_printf(2, "log end:   0x%04lx\n", log_end);
820                         debug_printf(2, "log start: 0x%04lx\n", log_start);
821                         log_data = &log->data[0];
822                         log_size = ARRAY_SIZE(log->data);
823                 } else {
824                         struct rest_log_response *log = buf;
825                         log_data = &log->data[0];
826                         log_size = ARRAY_SIZE(log->data);
827                 }
828                 debug_printf(3, "writing %ld bytes to resbuf[%d]\n",
829                                 log_size, total_bytes);
830                 memcpy(&resbuf[total_bytes], log_data, log_size);
831                 total_bytes += log_size;
832         }
833         // The last byte *should* be a null, and the 
834         // official software does not print it.
835         for (i = 0; i < total_bytes-1; i++) {
836                 int offset = (log_start+i)%total_bytes;
837                 char c = resbuf[offset];
838                 // the official software converts UNIX to DOS-style
839                 // line breaks, so we'll do the same
840                 if (c == '\n')
841                         printf("%c", '\r');
842                 printf("%c", c);
843         }
844         printf("\n");
845         // just some simple sanity checking to make sure what
846         // we are fetching looks valid
847         int null_bytes_left = 20;
848         if (resbuf[log_end] != 0) {
849                 debug_printf(2, "error: unexpected last byte (%ld/0x%lx) of log: %02x\n",
850                                 log_end, log_end, resbuf[log_end]);
851                 for (i=0; i<log_size; i++) {
852                         if (resbuf[i])
853                                 continue;
854                         if (null_bytes_left <= 0)
855                                 continue;
856                         null_bytes_left--;
857                         debug_printf(2, "null byte %d\n", i);
858                 }
859         }
860         free(resbuf);
861         return 0;
862 }
863
864 int atoo(char o)
865 {
866         if ((o >= '0') && (o <= '7'))
867                 return atoh(o);
868         return -1;
869 }
870
871 int octal_esc_to_chr(char *input) {
872         int i=0;
873         int ret = 0;
874         int len = strlen(input);
875
876         //intf("%s('%s')\n", __func__, input);
877         if (input[0] != '\\')
878                 return -1;
879         if (len < 4)
880                 return -1;
881
882         for (i=1; i < len ; i++) {
883                 if (i > 3)
884                         break;
885                 int tmp = atoo(input[i]);
886                 //intf("tmp: %d\n", tmp);
887                 if (tmp < 0)
888                         return tmp;
889                 ret <<= 3;
890                 ret += tmp;
891         }
892         return ret;
893 }
894
895 char *replace_escapes(char *str)
896 {
897         int i;
898         int output = 0;
899         debug_printf(4, "%s(%s)\n", __func__, str);
900         for (i=0; i < strlen(str); i++) {
901                 int esc = octal_esc_to_chr(&str[i]);
902                 if (esc >= 0) {
903                         str[output++] = esc;
904                         i += 3;
905                         continue;
906                 }
907                 str[output++] = str[i];
908         }
909         str[output] = '\0';
910         debug_printf(4, "'%s' %d\n", str, output);
911         return str;
912 }
913
914 #define LINEBUFSZ 1024
915 void locate_eyefi_mount(void)
916 {
917         char line[LINEBUFSZ];
918         FILE *mounts = fopen("/proc/mounts", "r");
919
920         char dev[LINEBUFSZ];
921         char mnt[LINEBUFSZ];
922         char fs[LINEBUFSZ];
923         char opt[LINEBUFSZ];
924         int foo;
925         int bar;
926         while (fgets(&line[0], 1023, mounts)) {
927                 int read;
928                 read = sscanf(&line[0], "%s %s %s %s %d %d",
929                                 &dev[0], &mnt[0], &fs[0], &opt[0],
930                                 &foo, &bar);
931                 // only look at fat filesystems:
932                 if (strcmp(fs, "msdos") && strcmp(fs, "vfat")) {
933                         debug_printf(2, "fs at '%s' is not fat, skipping...\n", mnt);
934                         continue;
935                 }
936                 // Linux's /proc/mounts has spaces like this \040
937                 replace_escapes(&mnt[0]);
938                 strcpy(&eyefi_mount[0], &mnt[0]);
939                 char *file = eyefi_file(REQM);
940                 debug_printf(2, "looking for EyeFi file here: '%s'\n", file);
941
942                 struct stat statbuf;
943                 int statret;
944                 statret = stat(file, &statbuf);
945                 free(file);
946                 if (statret)
947                         continue;
948                 debug_printf(1, "located EyeFi card at: %s\n", eyefi_mount);
949                 break;
950         }
951         fclose(mounts);
952 }
953
954 void usage(void)
955 {
956         printf("Usage:\n");
957         printf("  eyefitest [OPTIONS]\n");
958         printf("  -a ESSID      add network (implies test unless --force)\n");
959         printf("  -t ESSID      test network\n");
960         printf("  -p KEY        set WPA key for add/test\n");
961         printf("  -r ESSID      remove network\n");
962         printf("  -s            scan for networks\n");
963         printf("  -c            list configured networks\n");
964         printf("  -b            reboot card\n");
965         printf("  -d level      set debugging level (default: 1)\n");
966         printf("  -k            print card unique key\n");
967         printf("  -l            dump card log\n");
968         printf("  -m            print card mac\n");
969         exit(4);
970 }
971
972 int main(int argc, char **argv)
973 {
974         if (argc == 1)
975                 usage();
976
977         debug_printf(3, "%s starting...\n", argv[0]);
978         
979         locate_eyefi_mount();
980
981         //static int passed_wep = 0;
982         //static int passed_wpa = 0;
983         static int force = 0;
984         static struct option long_options[] = {
985                 //{"wep", 'x', &passed_wep, 1},
986                 //{"wpa", 'y', &passed_wpa, 1},
987                 {"force", 0, &force, 1},
988                 {"help", 'h', NULL, 1},
989         };
990
991         int option_index;
992         char c;
993         char *essid = NULL;
994         char *passwd = NULL;
995         char network_action = 0;
996         debug_printf(3, "about to parse arguments\n");
997         while ((c = getopt_long_only(argc, argv, "a:bcd:klmp:r:st:",
998                         &long_options[0], &option_index)) != -1) {
999                 debug_printf(3, "argument: '%c' %d optarg: '%s'\n", c, c, optarg);
1000                 switch (c) {
1001                 case 0:
1002                         // was a long argument
1003                         break;
1004                 case 'a':
1005                 case 't':
1006                 case 'r':
1007                         essid = optarg;
1008                         network_action = c;
1009                         break;
1010                 case 'b':
1011                         reboot_card();
1012                         break;
1013                 case 'c':
1014                         print_configured_nets();
1015                         break;
1016                 case 'd':
1017                         debug_level = atoi(optarg);
1018                         break;
1019                 case 'k':
1020                         print_card_key();
1021                         break;
1022                 case 'l':
1023                         get_log();
1024                         break;
1025                 case 'm':
1026                         print_card_mac();
1027                         break;
1028                 case 'p':
1029                         passwd = optarg;
1030                         break;
1031                 case 's':
1032                         scan_print_nets();
1033                         break;
1034                 case 'h':
1035                 default:
1036                         usage();
1037                         break;
1038                 }
1039         }
1040         debug_printf(3, "after arguments essid: '%s' passwd: '%s'\n", essid, passwd);
1041         if (network_action && essid) {
1042                 int ret = 0;
1043                 switch (network_action) {
1044                 case 't':
1045                         ret = try_connection_to(essid, passwd);
1046                         break;
1047                 case 'a':
1048                         if (!force) {
1049                                 ret = try_connection_to(essid, passwd);
1050                         } else {
1051                                 debug_printf(1, "forced: skipping network test\n");
1052                         }
1053                         if (ret) {
1054                                 printf("Error connecting to network '%s', not adding.\n", essid);
1055                                 printf("use --force to override\n");
1056                                 break;
1057                         }
1058                         add_network(essid, passwd);
1059                         break;
1060                 case 'r':
1061                         remove_network(essid);
1062                         break;
1063                 }
1064         }
1065         return 0;
1066 }
1067
1068