Titan Core Initial Contribution
[deliverable/titan.core.git] / license / license_gen.c
1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2000-2014 Ericsson Telecom AB
3 // All rights reserved. This program and the accompanying materials
4 // are made available under the terms of the Eclipse Public License v1.0
5 // which accompanies this distribution, and is available at
6 // http://www.eclipse.org/legal/epl-v10.html
7 ///////////////////////////////////////////////////////////////////////////////
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <sys/wait.h>
16
17 #include <mysql.h>
18
19 #include <openssl/rand.h>
20 #include <openssl/sha.h>
21 #include <openssl/dsa.h>
22 #include <openssl/pem.h>
23 #include <openssl/err.h>
24
25 #include "../common/memory.h"
26 #include "../common/license.h"
27
28 #define MYSQL_HOST "mwlx122.eth.ericsson.se"
29 #define MYSQL_USER "ttcn3"
30 #define MYSQL_PASSWORD "ttcn3"
31 #define MYSQL_DATABASE "ttcn3"
32
33 #define LICENSE_DIR "/mnt/TTCN/license"
34 #define PRIVATE_KEY LICENSE_DIR "/key.pem"
35
36 #define SECS_IN_DAY 86400
37
38 __attribute__ ((__format__ (__printf__, 1, 2), __noreturn__))
39 static void error(const char *fmt, ...)
40 {
41 va_list pvar;
42 fputs("ERROR: ", stderr);
43 va_start(pvar, fmt);
44 vfprintf(stderr, fmt, pvar);
45 va_end(pvar);
46 fputc('\n', stderr);
47 exit(EXIT_FAILURE);
48 }
49
50 /* Big-endian */
51 static void encode_int(unsigned char *to, unsigned int from)
52 {
53 to[3] = from & 0xFF;
54 from >>= 8;
55 to[2] = from & 0xFF;
56 from >>= 8;
57 to[1] = from & 0xFF;
58 from >>= 8;
59 to[0] = from & 0xFF;
60 }
61
62 static void encode_string(char *to, const char *from, size_t length)
63 {
64 strncpy(to, from, length);
65 }
66
67 static void encode_license(license_raw *to, const license_struct *from)
68 {
69 memset(to, 0, sizeof(*to));
70 encode_int(to->unique_id, from->unique_id);
71 encode_string(to->licensee_name, from->licensee_name,
72 sizeof(to->licensee_name));
73 encode_string(to->licensee_email, from->licensee_email,
74 sizeof(to->licensee_email));
75 encode_string(to->licensee_company, from->licensee_company,
76 sizeof(to->licensee_company));
77 encode_string(to->licensee_department, from->licensee_department,
78 sizeof(to->licensee_department));
79 encode_int(to->valid_from, from->valid_from);
80 encode_int(to->valid_until, from->valid_until);
81 encode_int(to->host_id, from->host_id);
82 encode_string(to->login_name, from->login_name, sizeof(to->login_name));
83 encode_int(to->from_major, from->from_major);
84 encode_int(to->from_minor, from->from_minor);
85 encode_int(to->from_patchlevel, from->from_patchlevel);
86 encode_int(to->to_major, from->to_major);
87 encode_int(to->to_minor, from->to_minor);
88 encode_int(to->to_patchlevel, from->to_patchlevel);
89 encode_int(to->feature_list, from->feature_list);
90 encode_int(to->limitation_type, from->limitation_type);
91 encode_int(to->max_ptcs, from->max_ptcs);
92 }
93
94 static void sign_license(license_raw *lptr, const char *dsa_key_file)
95 {
96 unsigned char message_digest[20];
97 SHA_CTX sha_ctx;
98 unsigned int signature_len = sizeof(lptr->dsa_signature);
99 DSA *dsa;
100
101 FILE *fp = fopen(dsa_key_file, "r");
102 if (fp == NULL) {
103 error("Cannot open DSA private key file `%s' for reading: %s",
104 dsa_key_file, strerror(errno));
105 }
106
107 dsa = PEM_read_DSAPrivateKey(fp, NULL, NULL, NULL);
108 fclose(fp);
109
110 if (dsa == NULL) {
111 error("Cannot read DSA private key from `%s': %s",
112 dsa_key_file, ERR_error_string(ERR_get_error(), NULL));
113 }
114
115 SHA1_Init(&sha_ctx);
116 SHA1_Update(&sha_ctx, lptr, sizeof(*lptr) - sizeof(lptr->dsa_signature));
117 SHA1_Final(message_digest, &sha_ctx);
118
119 if ((int)signature_len != DSA_size(dsa)) {
120 error("Invalid DSA signature size: %d", DSA_size(dsa));
121 }
122
123 if (!DSA_sign(0, message_digest, sizeof(message_digest),
124 lptr->dsa_signature, &signature_len, dsa)) {
125 error("DSA signature generation failed: %s",
126 ERR_error_string(ERR_get_error(), NULL));
127 }
128
129 DSA_free(dsa);
130 }
131
132 static void write_license(const char *file_name, const license_raw *lptr)
133 {
134 FILE *fp = fopen(file_name, "w");
135 if (fp == NULL) {
136 error("Cannot open license file `%s' for writing: %s", file_name,
137 strerror(errno));
138 }
139 if (!PEM_write(fp, "TTCN-3 LICENSE FILE", "", (unsigned char *)lptr,
140 sizeof(*lptr))) {
141 error("Writing to license file `%s' failed: %s", file_name,
142 ERR_error_string(ERR_get_error(), NULL));
143 }
144 fclose(fp);
145 }
146
147 static int privileged_user = 0;
148 static uid_t my_uid = -1;
149
150 static void check_user(void)
151 {
152 if (geteuid() != 45719) {
153 error("This program must have set-uid to user etccadmi1 (uid 45719)");
154 }
155 my_uid = getuid();
156 switch (my_uid) {
157 case 45719: /* etccadmi1 */
158 privileged_user = 1;
159 case 34217: /* ethjra */
160 case 34385: /* ethgasz */
161 break;
162 default:
163 error("You are not allowed to use this program");
164 }
165 }
166
167 static void connect_database(MYSQL *mysql)
168 {
169 if (!mysql_init(mysql)) {
170 error("MySQL structure initialization failed");
171 }
172 if (!mysql_real_connect(mysql, MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD,
173 MYSQL_DATABASE, 0, NULL, 0)) {
174 error("MySQL database connection failed: %s", mysql_error(mysql));
175 } else {
176 fputs("Connected to MySQL database.\n", stderr);
177 }
178 }
179
180 static void disconnect_database(MYSQL *mysql)
181 {
182 mysql_close(mysql);
183 fputs("Disconnected from MySQL database.\n", stderr);
184 }
185
186 static void fill_from_database_c(MYSQL *mysql, license_struct *plic, int unique_id)
187 {
188 MYSQL_RES *result;
189 my_ulonglong num_rows;
190 unsigned int i, num_fields;
191 MYSQL_FIELD *fields;
192 MYSQL_ROW row;
193 time_t current_time = time(NULL);
194 char *query = mprintf("SELECT * FROM licenses WHERE unique_id = %d", unique_id);
195 if (mysql_query(mysql, query)) {
196 error("Execution of MySQL query `%s' failed: %s", query,
197 mysql_error(mysql));
198 }
199 Free(query);
200
201 result = mysql_store_result(mysql);
202 if (result == NULL) {
203 error("MySQL query result fetching failed: %s", mysql_error(mysql));
204 }
205
206 num_rows = mysql_num_rows(result);
207 if (num_rows <= 0) {
208 error("There is no license in the database with unique id %d",
209 unique_id);
210 } else if (num_rows > 1) {
211 error("There are %llu licenses in the database with unique id %d",
212 num_rows, unique_id);
213 }
214
215 num_fields = mysql_num_fields(result);
216 fields = mysql_fetch_fields(result);
217 row = mysql_fetch_row(result);
218
219 fprintf(stderr, "License data was fetched for unique id %d.\n", unique_id);
220
221 for (i = 0; i < num_fields; i++) {
222 const char *field_name = fields[i].name;
223 if (!strcmp(field_name, "unique_id")) {
224 plic->unique_id = atoi(row[i]);
225 } else if (!strcmp(field_name, "licensee_name")) {
226 plic->licensee_name = row[i] != NULL ? row[i] : "";
227 } else if (!strcmp(field_name, "licensee_email")) {
228 plic->licensee_email = row[i] != NULL ? row[i] : "";
229 } else if (!strcmp(field_name, "contact_name")) {
230 plic->contact_name = row[i] != NULL ? row[i] : "";
231 } else if (!strcmp(field_name, "contact_email")) {
232 plic->contact_email = row[i] != NULL ? row[i] : "";
233 } else if (!strcmp(field_name, "send_to")) {
234 if (!strcmp(row[i], "Contact")) plic->send_to = SENDTO_CONTACT;
235 else if (!strcmp(row[i], "Both")) plic->send_to = SENDTO_BOTH;
236 else /*if (!strcmp(row[i], "Licensee"))*/ plic->send_to = SENDTO_LICENSEE;
237 /* SENDTO_LICENSEE is the default */
238 } else if (!strcmp(field_name, "licensee_company")) {
239 plic->licensee_company = row[i] != NULL ? row[i] : "";
240 } else if (!strcmp(field_name, "licensee_department")) {
241 plic->licensee_department = row[i] != NULL ? row[i] : "";
242 } else if (!strcmp(field_name, "valid_from")) {
243 int year, month, day;
244 struct tm tm_struct;
245 if (sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day) != 3) {
246 error("Invalid date format: `%s'", row[i]);
247 }
248 tm_struct.tm_year = year - 1900;
249 tm_struct.tm_mon = month - 1;
250 tm_struct.tm_mday = day;
251 tm_struct.tm_hour = 0;
252 tm_struct.tm_min = 0;
253 tm_struct.tm_sec = 0;
254 tm_struct.tm_isdst = -1;
255 plic->valid_from = mktime(&tm_struct);
256 } else if (!strcmp(field_name, "valid_until")) {
257 int year, month, day;
258 struct tm tm_struct;
259 if (sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day) != 3) {
260 error("Invalid date format: `%s'", row[i]);
261 }
262 tm_struct.tm_year = year - 1900;
263 tm_struct.tm_mon = month - 1;
264 tm_struct.tm_mday = day;
265 tm_struct.tm_hour = 23;
266 tm_struct.tm_min = 59;
267 tm_struct.tm_sec = 59;
268 tm_struct.tm_isdst = -1;
269 plic->valid_until = mktime(&tm_struct);
270 } else if (!strcmp(field_name, "host_id")) {
271 if (row[i] == NULL || sscanf(row[i], "%lx", &plic->host_id) != 1)
272 plic->host_id = 0;
273 } else if (!strcmp(field_name, "login_name")) {
274 plic->login_name = row[i] != NULL ? row[i] : "";
275 } else if (!strcmp(field_name, "from_major")) {
276 plic->from_major = atoi(row[i]);
277 } else if (!strcmp(field_name, "from_minor")) {
278 plic->from_minor = atoi(row[i]);
279 } else if (!strcmp(field_name, "from_patchlevel")) {
280 plic->from_patchlevel = atoi(row[i]);
281 } else if (!strcmp(field_name, "to_major")) {
282 plic->to_major = atoi(row[i]);
283 } else if (!strcmp(field_name, "to_minor")) {
284 plic->to_minor = atoi(row[i]);
285 } else if (!strcmp(field_name, "to_patchlevel")) {
286 plic->to_patchlevel = atoi(row[i]);
287 } else if (!strcmp(field_name, "feature_ttcn3")) {
288 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_TTCN3;
289 } else if (!strcmp(field_name, "feature_asn1")) {
290 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_ASN1;
291 } else if (!strcmp(field_name, "feature_codegen")) {
292 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_CODEGEN;
293 } else if (!strcmp(field_name, "feature_raw")) {
294 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_RAW;
295 } else if (!strcmp(field_name, "feature_text")) {
296 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_TEXT;
297 } else if (!strcmp(field_name, "feature_ber")) {
298 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_BER;
299 } else if (!strcmp(field_name, "feature_per")) {
300 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_PER;
301 } else if (!strcmp(field_name, "feature_xer")) {
302 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_XER;
303 } else if (!strcmp(field_name, "feature_tpgen")) {
304 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_TPGEN;
305 } else if (!strcmp(field_name, "feature_single")) {
306 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_SINGLE;
307 } else if (!strcmp(field_name, "feature_mctr")) {
308 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_MCTR;
309 } else if (!strcmp(field_name, "feature_hc")) {
310 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_HC;
311 } else if (!strcmp(field_name, "feature_logformat")) {
312 if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_LOGFORMAT;
313 } else if (!strcmp(field_name, "limit_host")) {
314 if (!strcmp(row[i], "Yes")) plic->limitation_type |= LIMIT_HOST;
315 } else if (!strcmp(field_name, "limit_user")) {
316 if (!strcmp(row[i], "Yes")) plic->limitation_type |= LIMIT_USER;
317 } else if (!strcmp(field_name, "max_ptcs")) {
318 plic->max_ptcs = atoi(row[i]);
319 }
320 } /* next */
321
322 if (plic->valid_from > current_time) {
323 error("The beginning of validity is in the future: %s",
324 ctime(&plic->valid_from));
325 }
326
327 if (plic->valid_until < current_time) {
328 error("The license has already expired on %s",
329 ctime(&plic->valid_until));
330 } else if (plic->valid_until < current_time + SECS_IN_DAY) {
331 error("The license is valid for less than one day");
332 } else if (plic->valid_until > current_time + 380 * SECS_IN_DAY &&
333 !privileged_user) {
334 error("You are not authorized to generate a license that is valid for "
335 "more than one year (%ld days)",
336 (plic->valid_until - current_time) / SECS_IN_DAY);
337 }
338
339 if (plic->limitation_type & LIMIT_HOST && plic->host_id == 0) {
340 error("Cannot generate HOST limited license for zero host ID");
341 }
342
343 if (plic->limitation_type & LIMIT_USER && !strcmp(plic->login_name, "")) {
344 error("Cannot generate USER limited license for empty login name");
345 }
346
347 if (plic->limitation_type == 0 && !privileged_user) {
348 error("You are not authorized to generate unrestricted licenses");
349 }
350 mysql_free_result(result);
351 }
352
353 static void fill_from_database(MYSQL *mysql, license_raw *lraw, int unique_id)
354 {
355
356 license_struct lstr;
357 memset(&lstr, 0, sizeof(lstr));
358
359 fill_from_database_c(mysql, &lstr, unique_id);
360
361 encode_license(lraw, &lstr);
362
363 }
364
365 static const char *get_email_address(void)
366 {
367 switch (my_uid) {
368 case 34217: /* ethjra */
369 return "Julianna.Rozsa@ericsson.com";
370 case 34385: /* ethgasz */
371 return "Gabor.Szalai@ericsson.com";
372 case 45719: /* etccadmi1 */
373 return "Julianna.Rozsa@ericsson.com";
374 default:
375 return "???";
376 }
377 }
378
379 static const char *get_first_name(void)
380 {
381 switch (my_uid) {
382 case 34217: /* ethjra */
383 return "Julianna";
384 case 34385: /* ethgasz */
385 return "Gábor";
386 case 45719: /* etccadmi1 */
387 return "Admin";
388 default:
389 return "???";
390 }
391 }
392
393 static const char *get_email_signature(void)
394 {
395 switch (my_uid) {
396 case 34217: /* ethjra */
397 return
398 "Julianna Rózsa Ericsson Hungary Ltd.\n"
399 "Configuration Manager H-1117 Budapest, Irinyi József u.4-20.\n"
400 "Test Competence Center Phone: +36 1 437 7895\n"
401 "Julianna.Rozsa@ericsson.com Fax: +36 1 439 5176\n";
402 case 34385: /* ethgasz */
403 return
404 "Gábor Szalai Gabor.Szalai@ericsson.com\n"
405 "Test Competence Center Tel: +36-1-437-7591\n"
406 "Ericsson Telecommunications Ltd. Fax: +36-1-439-5176\n"
407 "H-1037 Budapest, Laborc u. 1., Hungary Mob: +36-30-743-7669\n"
408 "Intranet: http://ttcn.ericsson.se/ ECN: 831-7591\n";
409 case 45719: /* etccadmi1 */
410 return
411 "TCC License Administrator Julianna.Rozsa@ericsson.com\n"
412 "Test Competence Center \n"
413 "Ericsson Telecommunications Ltd. Fax: +36-1-439-5176\n"
414 "H-1037 Budapest, Laborc u. 1., Hungary \n"
415 "Intranet: http://ttcn.ericsson.se/ \n";
416 default:
417 return "";
418 }
419 }
420
421 static void mail_license(const license_struct *lstr, const license_raw *lptr)
422 {
423 /* administrator's email address */
424 const char *email_addr;
425 /* licensee's email address, or empty if sent only to contact */
426 const char *maybe_licensee_email = (SENDTO_CONTACT != lstr->send_to) ? lstr->licensee_email : "";
427 /* contact's email address, or empty if sent only to licensee */
428 const char *maybe_contact_email = (SENDTO_LICENSEE != lstr->send_to) ? lstr->contact_email : "";
429 char *sendmail_cmd, *first_name, *mime_boundary;
430 FILE *fp;
431 size_t i;
432 time_t current_time;
433 struct tm *formatted_time;
434 int return_status;
435
436 email_addr = get_email_address();
437
438 sendmail_cmd = mprintf("/usr/lib/sendmail %s %s %s", maybe_licensee_email, maybe_contact_email,
439 email_addr);
440 fp = popen(sendmail_cmd, "w");
441 Free(sendmail_cmd);
442 if (fp == NULL) error("Cannot mail license: %s", strerror(errno));
443
444 first_name = memptystr();
445 for (i = 0; lstr->licensee_name[i] != '\0'; i++) {
446 if (lstr->licensee_name[i] == ' ') {
447 if (first_name[0] != '\0') break;
448 } else first_name = mputc(first_name, lstr->licensee_name[i]);
449 }
450 mime_boundary = mcopystr("__");
451 for (i = 0; i < 16; i++) {
452 unsigned char uc;
453 if (!RAND_pseudo_bytes(&uc, 1)) error("Random number generation "
454 "failed: %s", ERR_error_string(ERR_get_error(), NULL));
455 mime_boundary = mputprintf(mime_boundary, "%02X", uc);
456 }
457 mime_boundary = mputstr(mime_boundary, "__");
458
459 fprintf(fp, "From: TTCN-3 License Generator <%s>\n"
460 "To: %s <%s>\n"
461 "Subject: TTCN-3 license key\n"
462 "MIME-Version: 1.0\n"
463 "Content-Type: multipart/mixed; boundary=\"%s\"\n"
464 "\n"
465 "This is a multi-part message in MIME format.\n"
466 "--%s\n"
467 "Content-Type: text/plain; charset=ISO-8859-1\n"
468 "\n"
469 "Dear %s,\n"
470 "\n", email_addr, lstr->licensee_name, lstr->licensee_email,
471 mime_boundary, mime_boundary, first_name);
472 Free(first_name);
473
474 current_time = time(NULL);
475 formatted_time = localtime(&lstr->valid_until);
476 if (formatted_time == NULL) {
477 error("Cannot format time: %s", strerror(errno));
478 }
479 if (current_time - lstr->valid_from < SECS_IN_DAY) {
480 fprintf(fp, "this is your license key for the TTCN-3 "
481 "Test Executor. Please save the entire attachment "
482 "(including the header and trailing lines) and "
483 "follow the instructions of the Installation Guide "
484 "(Section 4.1.1) to proceed with the installation.\n"
485 "\n"
486 "The Unique ID of your license key is %d. Please refer to "
487 "this number if you have any licensing-related problems.\n"
488 "\n"
489 "The license key is valid till %04d-%02d-%02d beginning from today.\n"
490 "\n"
491 "Do not hesitate to contact us if you have any questions about "
492 "the TTCN-3 language or the test executor.\n", lstr->unique_id,
493 formatted_time->tm_year + 1900, formatted_time->tm_mon + 1,
494 formatted_time->tm_mday);
495 } else {
496 fprintf(fp, "your existing license key with Unique ID %d has been "
497 "prolonged so that it will expire on %04d-%02d-%02d. "
498 "Please find the updated license file in the attachment. "
499 "The only thing you have to do is to replace your old "
500 "license file with this one.\n", lstr->unique_id,
501 formatted_time->tm_year + 1900, formatted_time->tm_mon + 1,
502 formatted_time->tm_mday);
503 }
504 fprintf(fp, "\n"
505 "With best regards,\n"
506 "%s\n"
507 "\n"
508 "PS: This is an automatically generated e-mail, please reply to its "
509 "sender in case of problems!\n"
510 "\n", get_first_name());
511 if (SENDTO_CONTACT == lstr->send_to) {
512 fprintf (stderr, "%s, please forward this email to %s (%s)\n\n",
513 lstr->contact_name, lstr->licensee_name, lstr->licensee_email);
514 }
515 fprintf(fp, "--\n"
516 "%s"
517 "--%s\n"
518 "Content-Type: application/octet-stream\n"
519 "Content-Transfer-Encoding: 7bit\n"
520 "Content-Disposition: attachment; filename=\"license_%d.dat\"\n"
521 "\n", get_email_signature(), mime_boundary,
522 lstr->unique_id);
523 if (!PEM_write(fp, "TTCN-3 LICENSE FILE", "", (unsigned char*)lptr,
524 sizeof(*lptr))) {
525 error("Attaching license file failed: %s",
526 ERR_error_string(ERR_get_error(), NULL));
527 }
528 fprintf(fp, "\n"
529 "--%s--\n", mime_boundary);
530 Free(mime_boundary);
531 return_status = pclose(fp);
532 if (!WIFEXITED(return_status) ||
533 WEXITSTATUS(return_status) != EXIT_SUCCESS) {
534 error("Sendmail invocation failed");
535 }
536 }
537
538 static int get_unique_id(const char *str)
539 {
540 int unique_id = atoi(str);
541 if (unique_id <= 0) {
542 error("Invalid unique id: %s", str);
543 }
544 return unique_id;
545 }
546
547 int main(int argc, char *argv[])
548 {
549 license_struct lstr;
550 license_raw lraw;
551 char *file_name;
552 int unique_id;
553 MYSQL mysql;
554 int send_email;
555
556 fputs("License generator for the TTCN-3 Test Executor.\n", stderr);
557 check_user();
558
559 switch (argc) {
560 case 2:
561 unique_id = get_unique_id(argv[1]);
562 send_email = 0;
563 break;
564 case 3:
565 if (!strcmp(argv[1], "-m")) {
566 unique_id = get_unique_id(argv[2]);
567 send_email = 1;
568 break;
569 }
570 /* no break */
571 default:
572 error("Usage: %s [-m] unique_id", argv[0]);
573 }
574
575 init_openssl();
576
577 connect_database(&mysql);
578 fill_from_database(&mysql, &lraw, unique_id);
579 disconnect_database(&mysql);
580
581 sign_license(&lraw, PRIVATE_KEY);
582
583 file_name = mprintf(LICENSE_DIR "/license_%d.dat", unique_id);
584 write_license(file_name, &lraw);
585 fprintf(stderr, "License file %s was generated.\n", file_name);
586
587 fputs("License information:\n", stderr);
588 load_license_from_file(&lstr, file_name);
589 Free(file_name);
590 print_license(&lstr);
591 if (send_email) {
592 license_struct lstr2;
593 connect_database(&mysql);
594 fill_from_database_c(&mysql, &lstr2, unique_id);
595 disconnect_database(&mysql);
596 mail_license(&lstr2, &lraw);
597 fputs("E-mail was sent.\n", stderr);
598 /* do not free_license(lstr2) */
599 }
600 free_openssl();
601 free_license(&lstr);
602 check_mem_leak(argv[0]);
603
604 return EXIT_SUCCESS;
605 }
This page took 0.080547 seconds and 5 git commands to generate.