/* * Off-the-Record Messaging library * Copyright (C) 2004-2015 Ian Goldberg, Rob Smits, Chris Alexander, * Willy Lew, Lisa Du, Nikita Borisov * * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General * Public License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* system headers */ #include #include /* libgcrypt headers */ #include /* libotr headers */ #include "instag.h" #include "userstate.h" /* Forget the given instag. */ void otrl_instag_forget(OtrlInsTag* instag) { if (!instag) return; if (instag->accountname) free(instag->accountname); if (instag->protocol) free(instag->protocol); /* Re-link the list */ *(instag->tous) = instag->next; if (instag->next) { instag->next->tous = instag->tous; } free(instag); } /* Forget all instags in a given OtrlUserState. */ void otrl_instag_forget_all(OtrlUserState us) { while(us->instag_root) { otrl_instag_forget(us->instag_root); } } /* Fetch the instance tag from the given OtrlUserState associated with * the given account */ OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname, const char *protocol) { OtrlInsTag *p; for(p=us->instag_root; p; p=p->next) { if (!strcmp(p->accountname, accountname) && !strcmp(p->protocol, protocol)) { return p; } } return NULL; } /* Read our instance tag from a file on disk into the given * OtrlUserState. */ gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename) { gcry_error_t err; FILE *instf; /* Open the instance tag file. */ instf = fopen(filename, "rb"); if (!instf) { return gcry_error_from_errno(errno); } err = otrl_instag_read_FILEp(us, instf); fclose(instf); return err; } /* Read our instance tag from a file on disk into the given * OtrlUserState. The FILE* must be open for reading. */ gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf) { if (!instf) return gcry_error(GPG_ERR_NO_ERROR); OtrlInsTag *p; char storeline[1000]; size_t maxsize = sizeof(storeline); while(fgets(storeline, maxsize, instf)) { char *prevpos; char *pos; unsigned int instag = 0; p = malloc(sizeof(*p)); if (!p) { return gcry_error(GPG_ERR_ENOMEM); } /* Parse the line, which should be of the form: * accountname\tprotocol\t40_hex_nybbles\n */ prevpos = storeline; pos = strchr(prevpos, '\t'); if (!pos) { free(p); continue; } *pos = '\0'; pos++; p->accountname = malloc(pos - prevpos); if (!(p->accountname)) { free(p); return gcry_error(GPG_ERR_ENOMEM); } memmove(p->accountname, prevpos, pos - prevpos); prevpos = pos; pos = strchr(prevpos, '\t'); if (!pos) { free(p->accountname); free(p); continue; } *pos = '\0'; pos++; p->protocol = malloc(pos - prevpos); if (!(p->protocol)) { free(p->accountname); free(p); return gcry_error(GPG_ERR_ENOMEM); } memmove(p->protocol, prevpos, pos - prevpos); prevpos = pos; pos = strchr(prevpos, '\r'); if (!pos) pos = strchr(prevpos, '\n'); if (!pos) { free(p->accountname); free(p->protocol); free(p); continue; } *pos = '\0'; pos++; /* hex str of length 8 */ if (strlen(prevpos) != 8) { free(p->accountname); free(p->protocol); free(p); continue; } sscanf(prevpos, "%08x", &instag); if (instag < OTRL_MIN_VALID_INSTAG) { free(p->accountname); free(p->protocol); free(p); continue; } p->instag = instag; /* Link it up */ p->next = us->instag_root; if (p->next) { p->next->tous = &(p->next); } p->tous = &(us->instag_root); us->instag_root = p; } return gcry_error(GPG_ERR_NO_ERROR); } /* Generate a new instance tag for the given account and write to file */ gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename, const char *accountname, const char *protocol) { gcry_error_t err; FILE *instf; /* Open the instance tag file. */ instf = fopen(filename, "wb"); if (!instf) { return gcry_error_from_errno(errno); } err = otrl_instag_generate_FILEp(us, instf, accountname, protocol); fclose(instf); return err; } /* Return a new valid instance tag */ otrl_instag_t otrl_instag_get_new() { otrl_instag_t result = 0; while(result < OTRL_MIN_VALID_INSTAG) { otrl_instag_t * instag = (otrl_instag_t *)gcry_random_bytes( sizeof(otrl_instag_t), GCRY_STRONG_RANDOM); result = *instag; gcry_free(instag); } return result; } /* Generate a new instance tag for the given account and write to file * The FILE* must be open for writing. */ gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf, const char *accountname, const char *protocol) { OtrlInsTag *p; if (!accountname || !protocol) return gcry_error(GPG_ERR_NO_ERROR); p = (OtrlInsTag *)malloc(sizeof(OtrlInsTag)); p->accountname = strdup(accountname); p->protocol = strdup(protocol); p->instag = otrl_instag_get_new(); /* Add to our list in OtrlUserState */ p->next = us->instag_root; if (p->next) { p->next->tous = &(p->next); } p->tous = &(us->instag_root); us->instag_root = p; otrl_instag_write_FILEp(us, instf); return gcry_error(GPG_ERR_NO_ERROR); } /* Write our instance tags to a file on disk. */ gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename) { gcry_error_t err; FILE *instf; /* Open the instance tag file. */ instf = fopen(filename, "wb"); if (!instf) { return gcry_error_from_errno(errno); } err = otrl_instag_write_FILEp(us, instf); fclose(instf); return err; } /* Write our instance tags to a file on disk. * The FILE* must be open for writing. */ gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf) { OtrlInsTag *p; /* This line should be ignored when read back in, since there are no tabs. */ fprintf(instf, "# WARNING! You shouldn't copy this file to another" " computer. It is unnecessary and can cause problems.\n"); for(p=us->instag_root; p; p=p->next) { fprintf(instf, "%s\t%s\t%08x\n", p->accountname, p->protocol, p->instag); } return gcry_error(GPG_ERR_NO_ERROR); }