1 /*
2  * vim:tw=80:ai:tabstop=4:softtabstop=4:shiftwidth=4:expandtab
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * (C) Copyright Kevin Timmerman 2007
19  * (C) Copyright Phil Dibowitz 2007
20  */
21 
22 /* Platform-agnostic includes */
23 #define _SVID_SOURCE
24 #include <getopt.h>
25 #include <libconcord.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #ifdef _WIN32
31 /* Windows includes*/
32 #include <conio.h>
33 #include <winsock.h>
34 
35 /*
36  * (see below) Windows does not need this, since _getch already
37  * does what we need - just make set_canon do nothing
38  * Note that _getch returns '\r' when user hits the <Enter> key
39  */
set_canon(int flag)40 int set_canon(int flag)
41 {
42     return(1);
43 }
44 
45 #define read_key _getch
46 #define ENTER_KEY '\r'
47 
48 #else
49 /* NON-Windows */
50 
51 #include <strings.h>
52 #include <libgen.h>
53 #include <unistd.h>
54 #include <termios.h>
55 
56 /*
57  * set_canon in LINUX modifies stdin such that getchar behaves like
58  * _getch in Windows, i.e. the next key is returned immediately.
59  * Note that getchar returns '\n' when user hits the <Enter> key
60  */
set_canon(int flag)61 int set_canon(int flag)
62 {
63     struct termios t;
64 
65     tcgetattr(0, &t);
66     if (flag) {
67         t.c_lflag |= ICANON;
68     } else {
69         t.c_lflag &= ~ICANON;
70     }
71     tcsetattr(0, TCSANOW, &t);
72     return(1);
73 }
74 /* Thus, we can define: */
75 #define read_key getchar
76 #define ENTER_KEY '\n'
77 
78 #endif
79 
80 #define MAX_WAIT_FOR_BOOT 5
81 #define WAIT_FOR_BOOT_SLEEP 5
82 
83 #if defined(__APPLE__) || defined(__FreeBSD__)
84 #include <libgen.h>
85 #endif
86 
87 #define DEFAULT_CONFIG_FILENAME "Update.EZHex"
88 #define DEFAULT_CONFIG_FILENAME_BIN "update.bin"
89 #define DEFAULT_FW_FILENAME "firmware.EZUp"
90 #define DEFAULT_FW_FILENAME_BIN "firmware.bin"
91 #define DEFAULT_SAFE_FILENAME "safe.bin"
92 
93 const char * const VERSION = "1.2";
94 
95 struct options_t {
96     int binary;
97     int verbose;
98     int noweb;
99     int direct;
100     int noreset;
101     int force;
102 };
103 
104 enum {
105     MODE_UNSET,
106     MODE_CONNECTIVITY,
107     MODE_DUMP_CONFIG,
108     MODE_WRITE_CONFIG,
109     MODE_DUMP_FIRMWARE,
110     MODE_WRITE_FIRMWARE,
111     MODE_DUMP_SAFEMODE,
112     MODE_HELP,
113     MODE_LEARN_IR,
114     MODE_RESET,
115     MODE_GET_TIME,
116     MODE_SET_TIME,
117     MODE_PRINT_INFO,
118     MODE_VERSION
119 };
120 
121 /*
122  * Tack a colon onto the end, and then right-pad it out to 23-chars. This means
123  * we can have strings of up to 21 chars (21 chars, 1 colon, and 1 space before
124  * the percentages start).
125  */
print_stage_name(int stage_id)126 void print_stage_name(int stage_id)
127 {
128     const char *stage;
129     char output[24]; /* 21 + 1 + 1 + \0 */
130     stage = lc_cb_stage_str(stage_id);
131     strcpy(output, stage);
132     strcat(output, ":");
133     /* -22 is a 23-char-wide left-aligned string */
134     printf("%-22s", output);
135     return;
136 }
137 
138 /*
139  * Start callbacks
140  *
141  *   stage_id => integer of the stage
142  *   count => number of times we've been invoked
143  *   curr => current position (bytes, precent, etc.)
144  *   total => the number 'curr' is trying to reach
145  *   counter_type => what 'curr' represents (bytes, percent, etc.)
146  *   arg => an extra value we can ask libconcord to pass us.
147  *   stages => the array of stages that will be performed
148  */
cb_print_percent_status(uint32_t stage_id,uint32_t count,uint32_t curr,uint32_t total,uint32_t counter_type,void * arg,const uint32_t * stages)149 void cb_print_percent_status(uint32_t stage_id, uint32_t count, uint32_t curr,
150     uint32_t total, uint32_t counter_type, void *arg,
151     const uint32_t *stages)
152 {
153     if (stage_id == LC_CB_STAGE_NUM_STAGES) {
154 #if _DEBUG
155         printf("Num stages: %d\n", count);
156 #endif
157         return;
158     }
159 
160     if (count != 0) {
161         printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
162     } else {
163         print_stage_name(stage_id);
164     }
165 
166     if (counter_type == LC_CB_COUNTER_TYPE_BYTES) {
167         printf("%3i%%  %4i KiB", curr*100/total, curr>>10);
168     } else {
169         printf("%3i%%          ", curr*100/total);
170     }
171 
172     if (curr == total) {
173         printf("   done\n");
174     }
175 
176     fflush(stdout);
177 }
178 
179 /*
180  * Start helper functions
181  */
direct_warning()182 void direct_warning()
183 {
184     printf("WARNING: You have requested direct mode. This only affects\n");
185     printf("\t firmware updates. To do this safely you MUST be in\n");
186     printf("\t SAFEMODE! This is only for those devices where we\n");
187     printf("\t don't yet support live firmware updates. See the docs\n");
188     printf("\t for information on how to boot into safemode.\n");
189     printf("\t Press <enter> to continue.\n");
190     getchar();
191 }
192 
set_mode(int * mode,int val)193 void set_mode(int *mode, int val)
194 {
195     if (*mode == MODE_UNSET) {
196         *mode = val;
197     } else {
198         fprintf(stderr,
199             "ERROR: More than one mode specified. Please specify");
200         fprintf(stderr, " only one.\n");
201         exit(1);
202     }
203 }
204 
print_ir_burst(uint32_t length)205 void print_ir_burst(uint32_t length)
206 {
207     if (length < 250) {
208         printf("|");
209     } else if (length < 1000) {
210         printf("#");
211     } else {
212         printf("##");
213     }
214 }
215 
print_ir_space(uint32_t length)216 void print_ir_space(uint32_t length)
217 {
218     if (length < 250) {
219         printf(".");
220     } else if (length < 1000) {
221         printf("_");
222     } else if (length < 10000) {
223         printf("__");
224     } else {
225         printf("\n");
226     }
227 }
228 
print_received_ir_signal(uint32_t carrier_clock,uint32_t * ir_signal,uint32_t ir_signal_length,struct options_t * options)229 void print_received_ir_signal(uint32_t carrier_clock, uint32_t *ir_signal,
230     uint32_t ir_signal_length, struct options_t *options)
231 {
232     uint32_t index;
233     printf("\nASCII-graph of received IR signal:\n");
234     for (index = 0; index < ir_signal_length; index += 2){
235         print_ir_burst(ir_signal[index]);
236         print_ir_space(ir_signal[index+1]);
237     }
238     printf("\n");
239     printf("Carrier clock          : %u Hz\n", carrier_clock);
240     printf("Total mark/space pairs : %u\n\n", ir_signal_length/2);
241 #ifdef _DEBUG
242     /*
243      * full dump of new IR signal:
244      */
245     for (index=0; index < ir_signal_length; index += 2){
246         printf("\tP:%6u\tS:%6u\n", ir_signal[index],
247             ir_signal[index+1]);
248     }
249 #endif
250 }
251 
get_cmd(char * prompt,char * allowed,char def)252 char get_cmd(char *prompt, char *allowed, char def) {
253     char result = 0;
254     char got_key;
255     uint32_t index;
256     set_canon(0);
257     while ( result == 0 ) {
258         printf("%s (%c)?", prompt, def);
259         got_key = read_key();
260         printf("\n");
261         if (got_key == ENTER_KEY) {
262             result = def;
263         } else {
264             for (index = 0; index < strlen(allowed); index++) {
265                 if (allowed[index] == got_key) {
266                     result = got_key;
267                     break;
268                 }
269             }
270         }
271     }
272     set_canon(1);
273     return result;
274 }
275 
mode_string(int mode)276 char *mode_string(int mode)
277 {
278     switch (mode) {
279     case MODE_CONNECTIVITY:
280         return "Connectivity Check";
281         break;
282     case MODE_WRITE_CONFIG:
283         return "Write Configuration";
284         break;
285     case MODE_WRITE_FIRMWARE:
286         return "Write Firmware";
287         break;
288     case MODE_LEARN_IR:
289         return "Learn IR";
290         break;
291     default:
292         return "Unknown";
293         break;
294     }
295 }
296 
report_mode_mismatch(int mode,int file_mode,int force)297 void report_mode_mismatch(int mode, int file_mode, int force)
298 {
299     if (force) {
300         fprintf(stderr, "WARNING:");
301     } else {
302         fprintf(stderr, "ERROR:");
303     }
304     fprintf(stderr, " Requested mode is: %s\n", mode_string(mode));
305     fprintf(stderr, "       but file detected as: %s\n",
306             mode_string(file_mode));
307     fprintf(stderr, "Try not specifying a mode at all, I will figure it");
308     fprintf(stderr, " out for you.\n");
309 }
310 
311 /*
312  * In certain cases we don't require filenames, this provides
313  * sane defaults.
314  */
populate_default_filename(int mode,int binary,char ** file_name)315 void populate_default_filename(int mode, int binary, char **file_name)
316 {
317     switch (mode) {
318         case MODE_DUMP_CONFIG:
319             if (binary) {
320                 *file_name = (char *)
321                 strdup(DEFAULT_CONFIG_FILENAME_BIN);
322             } else {
323                 *file_name = (char *)
324                 strdup(DEFAULT_CONFIG_FILENAME);
325             }
326             break;
327 
328         case MODE_DUMP_FIRMWARE:
329             if (binary) {
330                 *file_name = (char *)
331                 strdup(DEFAULT_FW_FILENAME_BIN);
332             } else {
333                 *file_name = (char *)
334                 strdup(DEFAULT_FW_FILENAME);
335             }
336             break;
337 
338         case MODE_DUMP_SAFEMODE:
339             *file_name = (char *) strdup(DEFAULT_SAFE_FILENAME);
340             break;
341     }
342 }
343 
344 /*
345  * Parse our options.
346  */
parse_options(struct options_t * options,int * mode,char ** file_name,int argc,char * argv[])347 void parse_options(struct options_t *options, int *mode, char **file_name,
348                    int argc, char *argv[])
349 {
350     int tmpint, option_index;
351 
352     static struct option long_options[] = {
353         {"binary", no_argument, 0, 'b'},
354         {"dump-config", optional_argument, 0, 'c'},
355         {"write-config", required_argument, 0, 'C'},
356         {"direct", no_argument, 0, 'd'},
357         {"force", no_argument, 0, 0},
358         {"dump-firmware", optional_argument, 0, 'f'},
359         {"write-firmware", required_argument, 0, 'F'},
360         {"help", no_argument, 0, 'h'},
361         {"print-remote-info", no_argument, 0, 'i'},
362         {"learn-ir", required_argument, 0, 'l'},
363         {"reset", no_argument, 0, 'r'},
364         {"no-reset", no_argument, 0, 'R'},
365         {"dump-safemode", optional_argument, 0, 's'},
366         {"connectivity-test", required_argument, 0, 't'},
367         {"get-time", no_argument, 0, 'k' },
368         {"set-time", no_argument, 0, 'K' },
369         {"verbose", no_argument, 0, 'v'},
370         {"version", no_argument, 0, 'V'},
371         {"no-web", no_argument, 0, 'w'},
372         {0,0,0,0} /* terminating null entry */
373     };
374 
375     (*options).verbose = 0;
376     (*options).binary = 0;
377     (*options).noweb = 0;
378     (*options).direct = 0;
379     (*options).force = 0;
380     (*options).noreset = 0;
381 
382     *mode = MODE_UNSET;
383 
384     tmpint = 0;
385     option_index = 0;
386 
387     while ((tmpint = getopt_long(argc, argv, "bc::C:df::F:hil:rs::t:kKvVw",
388                 long_options, &option_index)) != EOF) {
389         switch (tmpint) {
390         case 0:
391             /* Long-only options go here */
392             if (!strcmp(long_options[option_index].name, "force")) {
393                 (*options).force = 1;
394                 break;
395             }
396             break;
397         case 'b':
398             (*options).binary = 1;
399             break;
400         case 'c':
401             set_mode(mode, MODE_DUMP_CONFIG);
402             if (optarg != NULL) {
403                 *file_name = optarg;
404             }
405             break;
406         case 'C':
407             if (optarg == NULL) {
408                 fprintf(stderr, "Missing config file to read from.\n");
409                 exit(1);
410             }
411             set_mode(mode, MODE_WRITE_CONFIG);
412             *file_name = optarg;
413             break;
414         case 'd':
415             (*options).direct = 1;
416             break;
417         case 'f':
418             set_mode(mode, MODE_DUMP_FIRMWARE);
419             if (optarg != NULL)
420                 *file_name = optarg;
421             break;
422         case 'F':
423             if (optarg == NULL) {
424                 fprintf(stderr, "Missing firmware file to read from.\n");
425                 exit(1);
426             }
427             set_mode(mode, MODE_WRITE_FIRMWARE);
428             *file_name = optarg;
429             break;
430         case 'h':
431             set_mode(mode, MODE_HELP);
432             break;
433         case 'i':
434             set_mode(mode, MODE_PRINT_INFO);
435             break;
436         case 'l':
437             if (optarg == NULL) {
438                 fprintf(stderr, "Missing config file to read from.\n");
439                 exit(1);
440             }
441             set_mode(mode, MODE_LEARN_IR);
442             *file_name = optarg;
443             break;
444         case 'r':
445             set_mode(mode, MODE_RESET);
446             break;
447         case 'R':
448             (*options).noreset = 1;
449             break;
450         case 's':
451             set_mode(mode, MODE_DUMP_SAFEMODE);
452             if (optarg != NULL)
453                 *file_name = optarg;
454             break;
455         case 't':
456             if (optarg == NULL) {
457                 fprintf(stderr, "Missing connectivity file to read from.\n");
458                 exit(1);
459             }
460             set_mode(mode, MODE_CONNECTIVITY);
461             *file_name = optarg;
462             break;
463         case 'k':
464             set_mode(mode, MODE_GET_TIME);
465             break;
466         case 'K':
467             set_mode(mode, MODE_SET_TIME);
468             break;
469         case 'v':
470             (*options).verbose = 1;
471             break;
472         case 'V':
473             set_mode(mode, MODE_VERSION);
474             break;
475         case 'w':
476             (*options).noweb = 1;
477             break;
478         default:
479             exit(1);
480         }
481     }
482 
483     if (optind + 1 < argc) {
484         fprintf(stderr, "Multiple arguments were");
485         fprintf(stderr, " specified after the options");
486         fprintf(stderr, " - not sure what to do,");
487         fprintf(stderr, " exiting\n");
488         exit(1);
489     } else if (optind < argc) {
490         /* one thing left over, lets hope its a filename */
491         *file_name = argv[optind];
492     }
493 
494 }
495 
help()496 void help()
497 {
498     printf("There are two ways to invoke this software. The primary");
499     printf(" way is \"automatic\nmode\" where you just pass in a file");
500     printf(" and the software figures out what to\ndo for you:\n");
501     printf("\tconcordance <file>\n\n");
502     printf("Or you can specify what to do:\n");
503     printf("\tconcordance <options>\n\n");
504 
505     printf("When specifying options, you must first choose a mode:\n\n");
506 
507     printf("   -c, --dump-config [<filename>]\n");
508     printf("\tRead the config from the remote and write it to a file.");
509     printf("\n\tIf no filename is specified, config.EZHex is used.\n\n");
510     printf("   -C, --write-config <filename>\n");
511     printf("\tRead a config from <filename> and write it to the");
512     printf(" remote.\n\n");
513     printf("   -f, --dump-firmware [<filename>]\n");
514     printf("\tRead firmware from the remote and write it to a file.\n");
515     printf("\tIf no filename is specified firmware.EZUp is used.\n\n");
516     printf("   -F, --write-firmware <filename>\n");
517     printf("\tRead firmware from <filename> and write it to the");
518     printf(" remote\n\n");
519     printf("   -h, --help\n");
520     printf("\tPrint this help message and exit.\n\n");
521     printf("   -i, --print-remote-info\n");
522     printf("\tPrint information about the remote. Additional");
523     printf(" information will\n\tbe printed if -v is also used.\n\n");
524     printf("   -k, --get-time\n");
525     printf("\t Get time from the remote\n\n");
526     printf("   -K, --set-time\n");
527     printf("\t Set the remote's time clock\n\n");
528     printf("   -l, --learn-ir <filename>\n");
529     printf("\t Learn IR from other remotes. Use <filename>.\n\n");
530     printf("   -r, --reset\n");
531     printf("\tReset (power-cycle) the remote control\n\n");
532     printf("   -s, --dump-safemode [<filename>]\n");
533     printf("\tRead the safemode firmware from the remote and write it");
534     printf(" to a file.\n\tIf no filename is psecified, safe.bin is");
535     printf(" used.\n\n");
536     printf("   -t, --connectivity-test <filename>\n");
537     printf("\tDo a connectivity test using <filename>\n\n");
538     printf("   -V, --version\n");
539     printf("\t Print the version and exit\n\n");
540 
541     printf("NOTE: If using the short options (e.g. -c), then *optional*");
542     printf(" arguments\nmust be adjacent: -c/path/to/file.");
543     printf(" Required arguments don't have this\nlimitation, and can");
544     printf(" be specified -C /path/to/file or -C/path/to/file.\n\n");
545 
546     printf("Additionally, you can specify options to adjust the behavior");
547     printf(" of the software:\n\n");
548 
549     printf("  -b, --binary-only\n");
550     printf("\tWhen dumping a config or firmware, this specifies to dump");
551     printf(" only the\n\tbinary portion. When use without a specific");
552     printf(" filename, the default\n\tfilename's extension is changed to");
553     printf(" .bin.\n");
554     printf("\tWhen writing a config or firmware, this specifies the");
555     printf(" filename\n\tpassed in has just the binary blob, not the");
556     printf(" XML.\n\n");
557 
558     printf("   --force\n");
559     printf("\tForce. This forces concordance to use the file the way\n");
560     printf("\tyou specified, even if it doesn't think that's the kind\n");
561     printf("\tof file it is. This is necessary for files dumped by\n");
562     printf("\tconcordance.\n\n");
563 
564     printf("  -R, --no-reset\n");
565     printf("\tFor config or firmware updates, do not reboot the device");
566     printf(" when done.\n\tThis is generally only for debugging.\n\n");
567 
568     printf("  -v, --verbose\n");
569     printf("\tEnable verbose output.\n\n");
570 
571     printf("  -w, --no-web\n");
572     printf("\tDo not attempt to talk to the website. This is useful");
573     printf(" for\n\tre-programming the remote from a saved file, or for");
574     printf(" debugging.\n\n");
575 
576 }
577 
578 
579 /*
580  * Begin functions to actually do work
581  */
learn_ir_commands(struct options_t * options,lc_callback cb,void * cb_arg)582 int learn_ir_commands(struct options_t *options, lc_callback cb, void *cb_arg)
583 {
584     int err = 0;
585     uint32_t carrier_clock = 0;
586     uint32_t *ir_signal = NULL;
587     uint32_t ir_signal_length = 0;
588     uint32_t index = 0;
589     char **key_names;
590     uint32_t key_names_length = 0;
591     char *post_string = NULL;
592     char user_cmd;
593 
594     err = get_key_names(&key_names, &key_names_length);
595     if ((err != 0) || (key_names_length == 0))
596         return err;
597 
598     printf("Received file contains %u key names to be learned.\n",
599         key_names_length);
600 
601     while (1) {
602         if (index >= key_names_length) {
603             index--;
604             printf("Last key in list!\n");
605         }
606         printf("\nKey name : <%s> : \n", key_names[index]);
607         user_cmd = get_cmd("[L]earn, [N]ext, [P]revious, [Q]uit", "LlHhNnPpQq",
608                            'L');
609         err = -1;
610         /* have no code yet */
611         switch (user_cmd) {
612             case 'L':
613             case 'l':
614                 /* learn from remote: */
615                 printf("press corresponding key ");
616                 printf("on original remote within 5 sec:\n");
617                 err = learn_from_remote(&carrier_clock,
618                     &ir_signal, &ir_signal_length,
619                     cb_print_percent_status, NULL);
620                 break;
621             case 'P':
622             case 'p':
623                 if (index > 0) {
624                     index--;
625                 } else {
626                     printf("First key in list!\n");
627                 }
628                 break;
629             case 'N':
630             case 'n':
631                 index++;
632                 break;
633             default:
634                 break;
635         }
636         if ( err == 0 ) {
637             /* have new IR code: */
638             if ((*options).verbose) {
639                 print_received_ir_signal(carrier_clock,
640                     ir_signal, ir_signal_length, options);
641             }
642             err = encode_for_posting(carrier_clock, ir_signal,
643                     ir_signal_length, &post_string);
644             /* done with learned signal, free memory: */
645             delete_ir_signal(ir_signal);
646         }
647 
648         if ( err == 0 ) {
649             /* have successfully encoded new code: */
650 #ifdef _DEBUG
651             if ((*options).verbose) {
652                 printf("%s\n\n", post_string );
653             }
654 #endif
655             if (!(*options).noweb) {
656                 user_cmd = get_cmd(
657                     "[U]pload new code, [R]etry same key, [N]ext key, [Q]uit",
658                     "UuRrNnQq", 'U');
659             } else {
660                 /* no upload: just skip to next key */
661                 user_cmd = 'N';
662             }
663             switch (user_cmd) {
664                 case 'U':
665                 case 'u':
666                     /*printf("Upload to website:   ");
667                     cb_print_percent_status(
668                         0, 0, 0, 1,
669                         LC_CB_COUNTER_TYPE_STEPS, NULL);
670                     */
671                     err = post_new_code(key_names[index],
672                         post_string, cb, cb_arg);
673                     if ( err == 0 ) {
674                     /*
675                         cb_print_percent_status(
676                             0, 1, 1, 1,
677                             LC_CB_COUNTER_TYPE_STEPS,
678                             NULL);
679                         printf("       done\n");*/
680                         index++;
681                     } else {
682                         printf("       failed\n");
683                     }
684                     break;
685                 case 'N':
686                 case 'n':
687                     index++;
688                     break;
689                 default:
690                     break;
691             }
692             /* done, free memory */
693             delete_encoded_signal(post_string);
694         } else {
695             if (err > 0) {
696                 fprintf(stderr, "\nERROR:Problemreceiving IR");
697                 fprintf(stderr, " signal:\n\t%s \n",
698                     lc_strerror(err));
699             }
700         }
701 
702         if (user_cmd == 'Q' || user_cmd == 'q') {
703             break;
704         }
705     }
706     /* done, free memory */
707     delete_key_names(key_names, key_names_length);
708     return 0;
709 }
710 
dump_config(struct options_t * options,char * file_name,lc_callback cb,void * cb_arg)711 int dump_config(struct options_t *options, char *file_name, lc_callback cb,
712                 void *cb_arg)
713 {
714     int err = 0;
715     uint8_t *config;
716     uint32_t size = 0;
717 
718     if ((err = read_config_from_remote(&config, &size, cb, NULL))) {
719         return err;
720     }
721 
722     if ((err = write_config_to_file(config, size, file_name,
723                                     (*options).binary))) {
724         return err;
725     }
726 
727     delete_blob(config);
728 
729     return 0;
730 }
731 
print_time(int action)732 void print_time(int action)
733 {
734     static const char * const dow[8] =
735     { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "" };
736 
737     if (action == 0) {
738         printf("Remote time is ");
739     } else {
740         printf("Remote time has been set to ");
741     }
742 
743     printf("%04i/%02i/%02i %s %02i:%02i:%02i %+i %s\n", get_time_year(),
744            get_time_month(), get_time_day(), dow[get_time_dow() & 7],
745            get_time_hour(), get_time_minute(), get_time_second(),
746            get_time_utc_offset(), get_time_timezone());
747 }
748 
upload_config(struct options_t * options,lc_callback cb,void * cb_arg)749 int upload_config(struct options_t *options, lc_callback cb, void *cb_arg)
750 {
751     int err;
752 
753     /*
754      * Tell the website we're going to start. This, it seems creates a
755      * session object on their side, because if you miss the pre-config
756      * communication, you get a no-session error.
757      */
758     if (!(*options).binary && !(*options).noweb) {
759         if ((err = post_preconfig(cb, cb_arg)))
760             return err;
761     }
762 
763     if ((err = update_configuration(cb, cb_arg, (*options).noreset))) {
764         return err;
765     }
766 
767     /* Tell the website we're done */
768     if (!(*options).binary && !(*options).noweb) {
769         if ((err = post_postconfig(cb, cb_arg))) {
770             return err;
771         }
772     }
773 
774     return 0;
775 }
776 
dump_safemode(char * file_name,lc_callback cb,void * cb_arg)777 int dump_safemode(char *file_name, lc_callback cb, void *cb_arg)
778 {
779     uint8_t * safe = 0;
780     uint32_t safe_size;
781     int err = 0;
782 
783     if ((err = read_safemode_from_remote(&safe, &safe_size, cb,
784             cb_arg))) {
785         delete_blob(safe);
786         return err;
787     }
788 
789     if ((err = write_safemode_to_file(safe, safe_size, file_name))) {
790         delete_blob(safe);
791         return err;
792     }
793 
794     delete_blob(safe);
795     return 0;
796 }
797 
upload_firmware(struct options_t * options,lc_callback cb,void * cb_arg)798 int upload_firmware(struct options_t *options, lc_callback cb, void *cb_arg)
799 {
800     int err;
801 
802     if ((err = is_fw_update_supported((*options).direct))) {
803         fprintf(stderr, "Sorry, firmware upgrades are not yet");
804         fprintf(stderr, " on your remote model yet.\n");
805         return err;
806     }
807 
808     if ((*options).direct) {
809         direct_warning();
810     } else {
811         if (is_config_safe_after_fw() != 0) {
812             printf("NOTE: A firmware upgrade, will erase your");
813             printf(" remote's config and you will need to update");
814             printf(" it. You may want to make a backup with -c");
815             printf(" or otherwise just use the website.\n");
816             printf("Press <enter> to continue.\n");
817             getchar();
818         }
819     }
820 
821     if ((err = update_firmware(cb, cb_arg, (*options).noreset,
822                                (*options).direct)))
823         return err;
824 
825     if (!(*options).binary && !(*options).noweb) {
826         if ((err = post_postfirmware(cb, cb_arg)))
827             return err;
828     }
829 
830     return 0;
831 }
832 
dump_firmware(struct options_t * options,char * file_name,lc_callback cb,void * cb_arg)833 int dump_firmware(struct options_t *options, char *file_name,
834     lc_callback cb, void *cb_arg)
835 {
836     int err = 0;
837     uint8_t *firmware = 0;
838     uint32_t firmware_size;
839 
840     if ((err = read_firmware_from_remote(&firmware, &firmware_size, cb,
841                                          cb_arg))) {
842         delete_blob(firmware);
843         return err;
844     }
845 
846     if ((err = write_firmware_to_file(firmware, firmware_size, file_name,
847                                       (*options).binary))) {
848         delete_blob(firmware);
849         return err;
850     }
851 
852     delete_blob(firmware);
853     return 0;
854 }
855 
print_version_info(struct options_t * options)856 int print_version_info(struct options_t *options)
857 {
858     const char *cn;
859     int used, total;
860 
861     printf("  Model: %s %s", get_mfg(), get_model());
862     cn = get_codename();
863 
864     if (strcmp(cn,"")) {
865         printf(" (%s)\n", cn);
866     } else {
867         printf("\n");
868     }
869 
870     if ((*options).verbose)
871         printf("  Skin: %i\n", get_skin());
872 
873     printf("  Firmware Version: %i.%i\n", get_fw_ver_maj(), get_fw_ver_min());
874 
875     if ((*options).verbose)
876         printf("  Firmware Type: %i\n", get_fw_type());
877 
878     printf("  Hardware Version: %i.%i.%i\n", get_hw_ver_maj(), get_hw_ver_min(),
879            get_hw_ver_mic());
880 
881     if ((*options).verbose) {
882         int size = get_flash_size();
883         printf("  External Flash: ");
884         if (size >> 10 != 0) {
885             printf("%i MiB", size >> 10);
886         } else {
887             printf("%i KiB", size);
888         }
889         printf(" - %02X:%02X %s\n", get_flash_mfg(), get_flash_id(),
890                get_flash_part_num());
891 
892         printf("  Architecture: %i\n", get_arch());
893         printf("  Protocol: %i\n", get_proto());
894 
895         printf("  Manufacturer: %s\n", get_hid_mfg_str());
896         printf("  Product: %s\n", get_hid_prod_str());
897         printf("  IRL, ORL, FRL: %i, %i, %i\n", get_hid_irl(), get_hid_orl(),
898                get_hid_frl());
899         printf("  USB VID: %04X\n", get_usb_vid());
900         printf("  USB PID: %04X\n", get_usb_pid());
901         printf("  USB Ver: %04X\n", get_usb_bcd());
902 
903        /*
904         * Certain MH remotes (but not all) such as the Harmony Touch use a
905         * different serial number format.  In this case, mh_get_serial() will
906         * return a non-zero-length string which will have the correct serial.
907         */
908         if (strlen(mh_get_serial()) != 0)
909             printf("  Serial Number: %s\n", mh_get_serial());
910         else
911             printf("  Serial Number: %s\n\t%s\n\t%s\n", get_serial(1),
912                    get_serial(2), get_serial(3));
913     }
914 
915     used = get_config_bytes_used();
916     total = get_config_bytes_total();
917     printf("  Config Flash Used: %i%% (%i of %i KiB)\n\n",
918            (used*100+99) / total, (used+1023)>>10, (total+1023)>>10);
919 
920     return 0;
921 }
922 
923 /*
924  * MAIN
925  */
main(int argc,char * argv[])926 int main(int argc, char *argv[])
927 {
928     struct options_t options;
929     char *file_name;
930     int mode, file_mode, err;
931 
932     printf("Concordance %s\n", VERSION);
933     printf("Copyright 2007 Kevin Timmerman and Phil Dibowitz\n");
934     printf("This software is distributed under the GPLv3.\n\n");
935 
936     file_name = NULL;
937     mode = MODE_UNSET;
938 
939     parse_options(&options, &mode, &file_name, argc, argv);
940 
941     /*
942      * Handle the few simple modes that don't require any files
943      * to be read in or any realy work to be done first...
944      */
945     if (mode == MODE_VERSION) {
946         exit(0);
947     }
948 
949     if (mode == MODE_HELP) {
950         help();
951         exit(0);
952     }
953 
954     /*
955      * We used to delay remote initialization until after all the figuring
956      * out the mode and read the file stuff... but with zwave support we
957      * need to know what type of remote we're dealing with early on.
958      */
959 
960     err = init_concord();
961     if (err != 0) {
962         fprintf(stderr, "ERROR: Couldn't initializing libconcord: %s\n",
963                 lc_strerror(err));
964         exit(1);
965     }
966 
967     /*
968       * Alright, at this point, if there's going to be a filename,
969       * we have one, so lets read the file.
970       */
971     if (file_name && (mode != MODE_DUMP_CONFIG && mode != MODE_DUMP_FIRMWARE &&
972         mode != MODE_DUMP_SAFEMODE)) {
973 
974         int type;
975         if ((err = read_and_parse_file(file_name, &type))) {
976             fprintf(stderr, "ERROR: Cannot read input file: %s\n", file_name);
977             exit(1);
978         }
979 
980         switch (type) {
981         case LC_FILE_TYPE_CONNECTIVITY:
982             file_mode = MODE_CONNECTIVITY;
983             break;
984         case LC_FILE_TYPE_CONFIGURATION:
985             file_mode = MODE_WRITE_CONFIG;
986             break;
987         case LC_FILE_TYPE_FIRMWARE:
988             file_mode = MODE_WRITE_FIRMWARE;
989             break;
990         case LC_FILE_TYPE_LEARN_IR:
991             file_mode = MODE_LEARN_IR;
992             break;
993         default:
994             fprintf(stderr, "ERROR: Couldn't determine filetype");
995             exit(1);
996             break;
997         }
998 
999         /*
1000          * If we don't have a mode, lets detect that mode based on
1001          * the file.
1002          */
1003         if (mode == MODE_UNSET) {
1004             mode = file_mode;
1005         } else if (mode != file_mode) {
1006             report_mode_mismatch(mode, file_mode, options.force);
1007             if (!options.force) {
1008                 exit(1);
1009             }
1010         }
1011     }
1012 
1013     /*
1014      * We have checked the parameters and the input file:
1015      * If we still don't know what to do, failure is the only option:
1016      */
1017     if (mode == MODE_UNSET) {
1018         fprintf(stderr, "ERROR: No mode requested. No work to do.\n");
1019         exit(1);
1020     }
1021 
1022     /*
1023      * The is..supported() functions return 0 for yes, so 1 is NO
1024      */
1025     if ((mode == MODE_DUMP_CONFIG && is_config_dump_supported()) ||
1026         ((mode == MODE_DUMP_FIRMWARE || mode == MODE_DUMP_SAFEMODE) &&
1027          is_fw_dump_supported())) {
1028         fprintf(stderr, "Sorry, that mode is not yet supported on your"
1029             " remote by libconcord.\n");
1030         exit(1);
1031     }
1032 
1033     /*
1034      * In a few special cases, we populate a default filename. NEVER
1035      * if we're are writing to the device, however.
1036      *
1037      * But wait - we already read in the file! Indeed, if we have a default
1038      * filename, then we'll be writing to the file, not reading it, which
1039      * is why we do this down here.
1040      */
1041     if (!file_name)  {
1042         populate_default_filename(mode, options.binary, &file_name);
1043     }
1044 
1045 
1046     /*
1047      * If we're in reset mode, not only do we not need to read and print
1048      * data from the device, but it may not even be fully functional, so
1049      * we probably want to do as little as possible. This we do this
1050      * near the beginning instead of the SWITCH below.
1051      *
1052      * Note that we don't look at or save the return value of reset_remote()
1053      * because the usb write for reset doesn't always return successful on
1054      * all remotes even though it works.
1055      */
1056     if (mode == MODE_RESET) {
1057         printf("Resetting...\n");
1058         reset_remote(NULL, NULL);
1059         goto cleanup;
1060     }
1061 
1062     /*
1063      * Get and print all the version info
1064      */
1065 
1066     err = get_identity(cb_print_percent_status, NULL);
1067     if (err != 0 && err != LC_ERROR_INVALID_CONFIG) {
1068         fprintf(stderr, "ERROR: failed to requesting identity\n");
1069         goto cleanup;
1070     }
1071     if (err == LC_ERROR_INVALID_CONFIG) {
1072         printf("WARNING: Invalid config found.\n");
1073     }
1074 
1075     /*
1076      * Now do whatever we've been asked to do
1077      */
1078 
1079     switch (mode) {
1080         case MODE_PRINT_INFO:
1081             err = print_version_info(&options);
1082             break;
1083 
1084         case MODE_CONNECTIVITY:
1085             if (!options.noweb) {
1086                 err = post_connect_test_success(cb_print_percent_status, NULL);
1087             }
1088             break;
1089 
1090         case MODE_DUMP_CONFIG:
1091             err = dump_config(&options, file_name, cb_print_percent_status,
1092                               NULL);
1093             if (err != 0) {
1094                 printf("Failed to dump config: %s\n", lc_strerror(err));
1095             }
1096             break;
1097 
1098         case MODE_WRITE_CONFIG:
1099             err = upload_config(&options, cb_print_percent_status, NULL);
1100             if (err != 0) {
1101                 printf("Failed to upload config: %s\n", lc_strerror(err));
1102             }
1103             break;
1104 
1105         case MODE_DUMP_FIRMWARE:
1106             err = dump_firmware(&options, file_name, cb_print_percent_status,
1107                                 NULL);
1108             if (err != 0) {
1109                 printf("Failed to dump firmware: %s\n", lc_strerror(err));
1110             }
1111             break;
1112 
1113         case MODE_WRITE_FIRMWARE:
1114             err = upload_firmware(&options, cb_print_percent_status, NULL);
1115             if (err != 0) {
1116                 printf("Failed to upload firmware: %s\n", lc_strerror(err));
1117             }
1118             break;
1119 
1120         case MODE_DUMP_SAFEMODE:
1121             err = dump_safemode(file_name, cb_print_percent_status, NULL);
1122             if (err != 0) {
1123                 printf("Failed to dump safemode: %s\n", lc_strerror(err));
1124             }
1125             break;
1126 
1127         case MODE_LEARN_IR:
1128             err = learn_ir_commands(&options, cb_print_percent_status, NULL);
1129             break;
1130 
1131         case MODE_GET_TIME:
1132             err = get_time();
1133             print_time(0);
1134             break;
1135 
1136         case MODE_SET_TIME:
1137             err = set_time(cb_print_percent_status, NULL);
1138             print_time(1);
1139             break;
1140 
1141         default:
1142             fprintf(stderr,
1143                 "ERROR: Got to a place I don't understand!\n");
1144             break;
1145     }
1146 
1147 cleanup:
1148 
1149     delete_opfile_obj();
1150 
1151     deinit_concord();
1152 
1153     if (err) {
1154         printf("Failed with error %i\n", err);
1155     } else {
1156         printf("Success!\n");
1157     }
1158 
1159 #ifdef _WIN32
1160     /* Shutdown WinSock */
1161     WSACleanup();
1162 #endif
1163 
1164 #ifdef _DEBUG
1165     printf("Press <enter> key to exit\n");
1166     getchar();
1167 #endif /* debug */
1168 
1169     return err;
1170 }
1171