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