int inplaceHeaderFilter(string& hdrs, const set<string>& headerfilter_list, FilterType f_type) {
   if (!hdrs.length() || !isActiveFilter(f_type))
	return 0;

    int res = 0;
    size_t start_pos = 0;
    while (start_pos<hdrs.length()) {
	size_t name_end, val_begin, val_end, hdr_end;
	if ((res = skip_header(hdrs, start_pos, name_end, val_begin,
			       val_end, hdr_end)) != 0) {
	    return res;
	}
	string hdr_name = hdrs.substr(start_pos, name_end-start_pos);
	transform(hdr_name.begin(), hdr_name.end(), hdr_name.begin(), ::tolower);
	bool erase = false;
	if (f_type == Whitelist) {
	    erase = headerfilter_list.find(hdr_name)==headerfilter_list.end();
	} else if (f_type == Blacklist) {
	    erase = headerfilter_list.find(hdr_name)!=headerfilter_list.end();
	}
	if (erase) {
	    DBG("erasing header '%s'\n", hdr_name.c_str());
	    hdrs.erase(start_pos, hdr_end-start_pos);
	} else {
	    start_pos = hdr_end;
	}
    }

    // todo: multi-line header support

    return res;
}
Esempio n. 2
0
void webget_file(const char *url, const char *host, short int portnum,
	const char *filename, char **filetype, char **encoding)
{
    char *ref;
    char *realdoc;
    int size;
    FILE *f;

    INFO("Récupération de %s depuis %s:%d", url, host, portnum);
    ref = webget(host, portnum, url, &size);
    INFO("Récupération réussie");

    /*
     *filetype = extract_filetype(ref);
     INFO("Type : %s", *filetype);

     *encoding = extract_fileencoding(ref);
     INFO("Encodage : %s", *encoding);
     */

    INFO("Stockage dans le fichier : %s", filename);
    realdoc = skip_header(ref, &size);
    f = fopen(filename, "w");
    fwrite(realdoc, sizeof(unsigned char), size, f);
    fclose(f);

    *filetype = file_type(filename);
    *encoding = malloc(sizeof(char));
    *encoding[0] = '\0';

    free(ref);
}
Esempio n. 3
0
static void
list_cmd(void) {
	char n[MAX_FNAME];
	FILE *f;
	int ch;

	log_it(RealUser, Pid, "LIST", User);
	if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
		errx(ERROR_EXIT, "path too long");
	}
	if (!(f = fopen(n, "r"))) {
		if (errno == ENOENT)
			errx(ERROR_EXIT, "no crontab for `%s'", User);
		else
			err(ERROR_EXIT, "Cannot open `%s'", n);
	}

	/* file is open. copy to stdout, close.
	 */
	Set_LineNum(1);
	skip_header(&ch, f);
	for (; EOF != ch;  ch = get_char(f))
		(void)putchar(ch);
	(void)fclose(f);
}
Esempio n. 4
0
/* Extract the flags from an otherwise unreadable Data Message. */
gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
	unsigned char *flagsp)
{
    char *otrtag, *endtag;
    unsigned char *rawmsg = NULL;
    unsigned char *bufp;
    size_t msglen, rawlen, lenp;
    unsigned char version;

    if (flagsp) *flagsp = 0;
    otrtag = strstr(datamsg, "?OTR:");
    if (!otrtag) {
	goto invval;
    }
    endtag = strchr(otrtag, '.');
    if (endtag) {
	msglen = endtag-otrtag;
    } else {
	msglen = strlen(otrtag);
    }

    /* Skip over the "?OTR:" */
    otrtag += 5;
    msglen -= 5;

    /* Base64-decode the message */
    rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen);   /* maximum possible */
    rawmsg = malloc(rawlen);
    if (!rawmsg && rawlen > 0) {
	return gcry_error(GPG_ERR_ENOMEM);
    }
    rawlen = otrl_base64_decode(rawmsg, otrtag, msglen);  /* actual size */

    bufp = rawmsg;
    lenp = rawlen;

    require_len(3);
    version = bufp[1];
    skip_header('\x03');

    if (version == 3) {
	require_len(8);
	bufp += 8; lenp -= 8;
    }

    if (version == 2 || version == 3) {
	require_len(1);
	if (flagsp) *flagsp = bufp[0];
	bufp += 1; lenp -= 1;
    }

    free(rawmsg);
    return gcry_error(GPG_ERR_NO_ERROR);

invval:
    free(rawmsg);
    return gcry_error(GPG_ERR_INV_VALUE);
}
Esempio n. 5
0
int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
{
	struct rpm_lead lead;
	unsigned pos;

	if (argv[1]) {
		xmove_fd(xopen(argv[1], O_RDONLY), rpm_fd);
	}
	xread(rpm_fd, &lead, sizeof(lead));

	/* Just check the magic, the rest is irrelevant */
	if (lead.magic != htonl(RPM_LEAD_MAGIC)) {
		bb_error_msg_and_die("invalid RPM magic");
	}

	/* Skip the signature header, align to 8 bytes */
	pos = skip_header();
	seek_by_jump(rpm_fd, (-(int)pos) & 7);

	/* Skip the main header */
	skip_header();

	//if (SEAMLESS_COMPRESSION)
	//	/* We need to know whether child (gzip/bzip/etc) exits abnormally */
	//	signal(SIGCHLD, check_errors_in_children);

	/* This works, but doesn't report uncompress errors (they happen in child) */
	setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1);
	if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
		bb_error_msg_and_die("error unpacking");

	if (ENABLE_FEATURE_CLEAN_UP) {
		close(rpm_fd);
	}

	if (SEAMLESS_COMPRESSION) {
		check_errors_in_children(0);
		return bb_got_signal;
	}
	return EXIT_SUCCESS;
}
Esempio n. 6
0
int main() 
{   
    char buf[BUF_SIZE];
    int socket_serveur, socket_client;
    if((socket_serveur = creer_serveur(8080)) == EXIT_FAILURE) {
        return EXIT_FAILURE;
    }

    initialiser_signaux();
    const char * message_bienvenue = "Bonjour, bienvenue sur le serveur\nle plus parfait du monde.\nSur votre droite, vous pourrez voir\nrien qui n\'est plus parfait que\nle serveur qui defie la perfection.\nSur votre gauche, pareil.\nNenufar.\nOgnon.\r\n";

    while (1) {
        socket_client = accept(socket_serveur, NULL, NULL);
        if (socket_client == -1) {
            perror("accept");
            return EXIT_FAILURE;
        }
        FILE *fsocket = fdopen(socket_client, "w+");
        switch (fork()) {
	case -1:
	    perror("fork");
	    return EXIT_FAILURE;
	case 0:
	    /* On peut maintenant dialoguer avec le client */
                
	    fgets_or_exit(buf, BUF_SIZE, fsocket);
	    http_request request;

	    int bad_request = parse_http_request(buf, &request);
	    skip_header(fsocket);
                
	    if (bad_request == 0) {
		send_response(fsocket, 400, "Bad Request", "Bad Request\r\n");
	    }
	    else if (request.method == HTTP_UNSUPPORTED) {
		send_response(fsocket, 405, "Method not allowed", "Method not allowed\r\n");
	    }
	    else if (strcmp(request.url, "/") == 0) {
		send_response(fsocket, 200, "OK", message_bienvenue);
	    }
	    else {
		send_response(fsocket, 404, "Not Found", "Not Found\r\n");
	    }
	    fclose(fsocket);
	    return EXIT_SUCCESS;
	default:
	    fclose(fsocket);
	    close(socket_client);
        }
    }

    return EXIT_SUCCESS;
}
Esempio n. 7
0
OscatsItemBank * read_items(FILE *f,
                            OscatsSpace *contSpace, OscatsSpace *binSpace)
{
  OscatsModel *model;
  OscatsAdministrand *item;
  // Create an item bank to store the items.
  // Setting the property "sizeHint" increases allocation efficiency.
  // Be sure to include NULL at the end of calls to g_object_new()!
  OscatsItemBank *bank = g_object_new(OSCATS_TYPE_ITEM_BANK,
                                      "sizeHint", N_ITEMS, NULL);
  OscatsDim dim;
  GString *str = g_string_new(NULL);
  gdouble a, b, c, slip, guess;
  guint i, count = 0;
  gchar attrStr[N_ATTR+1];  // The fixed-length buffer isn't exactly safe...
  
  skip_header(f);
  while (6 == fscanf(f, "%lf %lf %lf %lf %lf %s", &a, &b, &c, &slip, &guess,
                     attrStr))
  {
    // Create the item
    g_string_printf(str, "%d", ++count);
    item = g_object_new(OSCATS_TYPE_ITEM, "id", str->str, NULL);
    // Create the 3PL model
    dim = OSCATS_DIM_CONT;	// First continuous dimension
    model = oscats_model_new(OSCATS_TYPE_MODEL_L3P, contSpace, &dim, 1, NULL);
    oscats_model_set_param_by_name(model, "Discr.Cont.1", a);
    /* The difficulty parameter in the input file is specified as the
     * traditional IRT difficulty: logit P(x) = a(theta-b); but,
     * OscatsModelL3p uses the parameterization: logit P(x) = a*theta - b,
     * so we have to multiply here, accordingly.  */
    oscats_model_set_param_by_name(model, "Diff", a*b);
    oscats_model_set_param_by_name(model, "Guess", c);
    // Set the model (takes model's reference)
    oscats_administrand_set_model_by_name(item, "3PL", model);
    // Create the DINA model
    dim = OSCATS_DIM_BIN;	// First binary dimension
    for (i=0; attrStr[i] == '0' && i < N_ATTR; i++) dim++;
    model = oscats_model_new(OSCATS_TYPE_MODEL_DINA, binSpace, &dim, 1, NULL);
    oscats_model_set_param_by_name(model, "Slip", slip);
    oscats_model_set_param_by_name(model, "Guess", guess);
    // Set the model (takes model's reference)
    oscats_administrand_set_model_by_name(item, "DINA", model);
    // Add the item to the item bank
    oscats_item_bank_add_item(bank, item);
    g_object_unref(item);	// No longer needed here
  }
  if (count != N_ITEMS)
    g_warning("Expected %d items, but only read %d.", N_ITEMS, count);
  g_string_free(str, TRUE);
  return bank;
}
Esempio n. 8
0
/* No getopt required */
int rpm2cpio_main(int argc, char **argv)
{
	struct rpm_lead lead;
	int rpm_fd;
	unsigned char magic[2];

	if (argc == 1) {
		rpm_fd = STDIN_FILENO;
	} else {
		rpm_fd = bb_xopen(argv[1], O_RDONLY);
	}

	bb_xread_all(rpm_fd, &lead, sizeof(struct rpm_lead));
	if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
		bb_error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
	}

	/* Skip the signature header */
	skip_header(rpm_fd);
	lseek(rpm_fd, (8 - (lseek(rpm_fd, 0, SEEK_CUR) % 8)) % 8, SEEK_CUR);

	/* Skip the main header */
	skip_header(rpm_fd);

	bb_xread_all(rpm_fd, &magic, 2);
	if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
		bb_error_msg_and_die("Invalid gzip magic");
	}

	check_header_gzip(rpm_fd);
	if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) {
		bb_error_msg("Error inflating");
	}

	close(rpm_fd);

	return 0;
}
Esempio n. 9
0
void wi_bwmrestore(char *url, int len, char *boundary)
{
	char *buf;
	const char *error;
	int ok;
	int n;
	char tmp[64];

	check_id(url);

	tmp[0] = 0;
	buf = NULL;
	error = "Error reading file";
	ok = 0;

	if (!skip_header(&len)) {
		goto ERROR;
	}

	if ((len < 64) || (len > 10240)) {
		goto ERROR;
	}

	if ((buf = malloc(len)) == NULL) {
		error = "Not enough memory";
		goto ERROR;
	}

	n = web_read(buf, len);
	len -= n;

	sprintf(tmp, "%s.new", hfn);
	if (f_write(tmp, buf, n, 0, 0600) != n) {
		unlink(tmp);
		error = "Error writing temporary file";
		goto ERROR;
	}
	f_write("/var/tmp/rstats-load", NULL, 0, 0, 0600);
	killall("rstats", SIGHUP);
	sleep(1);

	error = NULL;
	rboot = 1;	// used as "ok"

ERROR:
	free(buf);
	web_eat(len);
	if (error != NULL) resmsg_set(error);
}
Esempio n. 10
0
int inplaceHeaderFilter(string& hdrs, const vector<FilterEntry>& filter_list) {
    if (!hdrs.length() || ! filter_list.size())
	return 0;

    DBG("applying %zd header filters\n", filter_list.size());

    for (vector<FilterEntry>::const_iterator fe =
	     filter_list.begin(); fe != filter_list.end(); fe++) {
	const set<string>& headerfilter_list = fe->filter_list;
	const FilterType& f_type = fe->filter_type; 

	if (!isActiveFilter(f_type))
	  continue;

	// todo: multi-line header support

	size_t start_pos = 0;
	while (start_pos<hdrs.length()) {
	    size_t name_end, val_begin, val_end, hdr_end;
	    int res;
	    if ((res = skip_header(hdrs, start_pos, name_end, val_begin,
				   val_end, hdr_end)) != 0) {
		return res;
	    }
	    string hdr_name = hdrs.substr(start_pos, name_end-start_pos);
	    std::transform(hdr_name.begin(), hdr_name.end(), hdr_name.begin(), ::tolower);
	    bool erase = false;
	    if (f_type == Whitelist) {
		erase = headerfilter_list.find(hdr_name)==headerfilter_list.end();
	    } else if (f_type == Blacklist) {
		erase = headerfilter_list.find(hdr_name)!=headerfilter_list.end();
	    }
	    if (erase) {
		DBG("erasing header '%s' by %s\n", hdr_name.c_str(), FilterType2String(f_type));
		hdrs.erase(start_pos, hdr_end-start_pos);
	    } else {
		start_pos = hdr_end;
	    }
	}

    }

    return 0;
}
Esempio n. 11
0
/* Prepare for a sequential iteration over the cache file. */
static krb5_error_code KRB5_CALLCONV
fcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
{
    krb5_fcc_cursor *fcursor;
    krb5_error_code ret;
    fcc_data *data = id->data;

    k5_cc_mutex_lock(context, &data->lock);

    fcursor = malloc(sizeof(krb5_fcc_cursor));
    if (fcursor == NULL) {
        k5_cc_mutex_unlock(context, &data->lock);
        return KRB5_CC_NOMEM;
    }
    if (OPENCLOSE(id)) {
        ret = open_cache_file(context, id, FCC_OPEN_RDONLY);
        if (ret) {
            free(fcursor);
            k5_cc_mutex_unlock(context, &data->lock);
            return ret;
        }
    }

    /* Make sure we start reading right after the primary principal */
    ret = skip_header(context, id);
    if (ret) {
        free(fcursor);
        goto done;
    }
    ret = skip_principal(context, id);
    if (ret) {
        free(fcursor);
        goto done;
    }

    fcursor->pos = fcc_lseek(data, 0, SEEK_CUR);
    *cursor = (krb5_cc_cursor)fcursor;

done:
    MAYBE_CLOSE(context, id, ret);
    k5_cc_mutex_unlock(context, &data->lock);
    return ret;
}
Esempio n. 12
0
int main(int argc, char **argv) {
    FILE *fp;
    
    char *s;
    int header_index=0;
    
    if((fp = fopen(argv[1], "r"))==NULL) {
        printf("Error while attempting to open:\n\n\t");
        printf("%s\n", argv[1]);
        return 1;
    }
    
    if(skip_header(fp, &header_index)) 
        return 1;
        
    s = retrieve_message(fp);
    display_message(s);
    
    free(s); 
    fclose(fp);
     
    return 0;
}
Esempio n. 13
0
static void
edit_cmd(void) {
	char n[MAX_FNAME], q[MAX_TEMPSTR];
	const char *editor;
	FILE *f;
	int ch, t, x;
	sig_t oint, oabrt, oquit, ohup;
	struct stat statbuf;
	struct utimbuf utimebuf;
	long mtimensec;
	WAIT_T waiter;
	PID_T pid, xpid;

	log_it(RealUser, Pid, "BEGIN EDIT", User);
	if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
		errx(ERROR_EXIT, "path too long");
	}
	if (!(f = fopen(n, "r"))) {
		if (errno != ENOENT) {
			err(ERROR_EXIT, "cannot open `%s'", n);
		}
		warnx("no crontab for `%s' - using an empty one", User);
		if (!(f = fopen(_PATH_DEVNULL, "r"))) {
			err(ERROR_EXIT, "cannot open `%s'", _PATH_DEVNULL);
		}
	}

	if (fstat(fileno(f), &statbuf) < 0) {
		warn("cannot stat crontab file");
		goto fatal;
	}
	utimebuf.actime = statbuf.st_atime;
	utimebuf.modtime = statbuf.st_mtime;
	mtimensec = statbuf.st_mtimensec;

	/* Turn off signals. */
	ohup = signal(SIGHUP, SIG_IGN);
	oint = signal(SIGINT, SIG_IGN);
	oquit = signal(SIGQUIT, SIG_IGN);
	oabrt = signal(SIGABRT, SIG_IGN);

	if (!glue_strings(Filename, sizeof Filename, _PATH_TMP,
	    "crontab.XXXXXXXXXX", '/')) {
		warnx("path too long");
		goto fatal;
	}
	if (-1 == (t = mkstemp(Filename))) {
		warn("cannot create `%s'", Filename);
		goto fatal;
	}
#ifdef HAS_FCHOWN
	x = fchown(t, MY_UID(pw), MY_GID(pw));
#else
	x = chown(Filename, MY_UID(pw), MY_GID(pw));
#endif
	if (x < 0) {
		warn("cannot chown `%s'", Filename);
		goto fatal;
	}
	if (!(NewCrontab = fdopen(t, "r+"))) {
		warn("cannot open fd");
		goto fatal;
	}

	Set_LineNum(1);

	skip_header(&ch, f);

	/* copy the rest of the crontab (if any) to the temp file.
	 */
	for (; EOF != ch; ch = get_char(f))
		(void)putc(ch, NewCrontab);
	(void)fclose(f);
	if (fflush(NewCrontab) < OK) {
		err(ERROR_EXIT, "cannot flush output for `%s'", Filename);
	}
	(void)utime(Filename, &utimebuf);
 again:
	rewind(NewCrontab);
	if (ferror(NewCrontab)) {
		warn("error while writing new crontab to `%s'", Filename);
 fatal:
		(void)unlink(Filename);
		exit(ERROR_EXIT);
	}

	if (((editor = getenv("VISUAL")) == NULL || *editor == '\0') &&
	    ((editor = getenv("EDITOR")) == NULL || *editor == '\0')) {
		editor = EDITOR;
	}

	/* we still have the file open.  editors will generally rewrite the
	 * original file rather than renaming/unlinking it and starting a
	 * new one; even backup files are supposed to be made by copying
	 * rather than by renaming.  if some editor does not support this,
	 * then don't use it.  the security problems are more severe if we
	 * close and reopen the file around the edit.
	 */

	switch (pid = fork()) {
	case -1:
		warn("cannot fork");
		goto fatal;
	case 0:
		/* child */
		if (setgid(MY_GID(pw)) < 0) {
			err(ERROR_EXIT, "cannot setgid(getgid())");
		}
		if (setuid(MY_UID(pw)) < 0) {
			err(ERROR_EXIT, "cannot setuid(getuid())");
		}
		if (chdir(_PATH_TMP) < 0) {
			err(ERROR_EXIT, "cannot chdir to `%s'", _PATH_TMP);
		}
		if (!glue_strings(q, sizeof q, editor, Filename, ' ')) {
			errx(ERROR_EXIT, "editor command line too long");
		}
		(void)execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, (char *)0);
		err(ERROR_EXIT, "cannot start `%s'", editor);
		/*NOTREACHED*/
	default:
		/* parent */
		break;
	}

	/* parent */
	for (;;) {
		xpid = waitpid(pid, &waiter, WUNTRACED);
		if (xpid == -1) {
			if (errno != EINTR)
				warn("waitpid() failed waiting for PID %ld "
				    "from `%s'", (long)pid, editor);
		} else if (xpid != pid) {
			warnx("wrong PID (%ld != %ld) from `%s'",
			    (long)xpid, (long)pid, editor);
			goto fatal;
		} else if (WIFSTOPPED(waiter)) {
			(void)kill(getpid(), WSTOPSIG(waiter));
		} else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
			warnx("`%s' exited with status %d\n",
			    editor, WEXITSTATUS(waiter));
			goto fatal;
		} else if (WIFSIGNALED(waiter)) {
			warnx("`%s' killed; signal %d (%score dumped)",
			    editor, WTERMSIG(waiter),
			    WCOREDUMP(waiter) ? "" : "no ");
			goto fatal;
		} else
			break;
	}
	(void)signal(SIGHUP, ohup);
	(void)signal(SIGINT, oint);
	(void)signal(SIGQUIT, oquit);
	(void)signal(SIGABRT, oabrt);

	if (fstat(t, &statbuf) < 0) {
		warn("cannot stat `%s'", Filename);
		goto fatal;
	}
	if (utimebuf.modtime == statbuf.st_mtime &&
	    mtimensec == statbuf.st_mtimensec) {
		warnx("no changes made to crontab");
		goto remove;
	}
	warnx("installing new crontab");
	switch (replace_cmd()) {
	case 0:
		break;
	case -1:
		for (;;) {
			(void)fpurge(stdin);
			(void)printf("Do you want to retry the same edit? ");
			(void)fflush(stdout);
			q[0] = '\0';
			(void) fgets(q, (int)sizeof(q), stdin);
			switch (q[0]) {
			case 'y':
			case 'Y':
				goto again;
			case 'n':
			case 'N':
				goto abandon;
			default:
				(void)printf("Enter Y or N\n");
			}
		}
		/*NOTREACHED*/
	case -2:
	abandon:
		warnx("edits left in `%s'", Filename);
		goto done;
	default:
		warnx("panic: bad switch() in replace_cmd()");
		goto fatal;
	}
 remove:
	(void)unlink(Filename);
 done:
	log_it(RealUser, Pid, "END EDIT", User);
}
Esempio n. 14
0
/* Accept an OTR Data Message in datamsg.  Decrypt it and put the
 * plaintext into *plaintextp, and any TLVs into tlvsp.  Put any
 * received flags into *flagsp (if non-NULL).  Put the current extra
 * symmetric key into extrakey (if non-NULL). */
gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
	ConnContext *context, const char *datamsg, unsigned char *flagsp,
	unsigned char *extrakey)
{
    char *otrtag, *endtag;
    gcry_error_t err;
    unsigned char *rawmsg = NULL;
    size_t msglen, rawlen, lenp;
    unsigned char *macstart, *macend;
    unsigned char *bufp;
    unsigned int sender_keyid, recipient_keyid;
    gcry_mpi_t sender_next_y = NULL;
    unsigned char ctr[8];
    size_t datalen, reveallen;
    unsigned char *data = NULL;
    unsigned char *nul = NULL;
    unsigned char givenmac[20];
    DH_sesskeys *sess;
    unsigned char version;

    *plaintextp = NULL;
    *tlvsp = NULL;
    if (flagsp) *flagsp = 0;
    otrtag = strstr(datamsg, "?OTR:");
    if (!otrtag) {
	goto invval;
    }
    endtag = strchr(otrtag, '.');
    if (endtag) {
	msglen = endtag-otrtag;
    } else {
	msglen = strlen(otrtag);
    }

    /* Skip over the "?OTR:" */
    otrtag += 5;
    msglen -= 5;

    /* Base64-decode the message */
    rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen);   /* maximum possible */
    rawmsg = malloc(rawlen);
    if (!rawmsg && rawlen > 0) {
	err = gcry_error(GPG_ERR_ENOMEM);
	goto err;
    }
    rawlen = otrl_base64_decode(rawmsg, otrtag, msglen);  /* actual size */

    bufp = rawmsg;
    lenp = rawlen;

    macstart = bufp;
    require_len(3);
    version = bufp[1];

    skip_header('\x03');

    if (version == 3) {
	require_len(8);
	bufp += 8; lenp -= 8;
    }

    if (version == 2 || version == 3) {
	require_len(1);
	if (flagsp) *flagsp = bufp[0];
	bufp += 1; lenp -= 1;
    }

    read_int(sender_keyid);
    read_int(recipient_keyid);
    read_mpi(sender_next_y);
    require_len(8);
    memmove(ctr, bufp, 8);
    bufp += 8; lenp -= 8;
    read_int(datalen);
    require_len(datalen);
    data = malloc(datalen+1);
    if (!data) {
	err = gcry_error(GPG_ERR_ENOMEM);
	goto err;
    }
    memmove(data, bufp, datalen);
    data[datalen] = '\0';
    bufp += datalen; lenp -= datalen;
    macend = bufp;
    require_len(20);
    memmove(givenmac, bufp, 20);
    bufp += 20; lenp -= 20;
    read_int(reveallen);
    require_len(reveallen);
    /* Just skip over the revealed MAC keys, which we don't need.  They
     * were published for deniability of transcripts. */
    bufp += reveallen; lenp -= reveallen;

    /* That should be everything */
    if (lenp != 0) goto invval;

    /* We don't take any action on this message (especially rotating
     * keys) until we've verified the MAC on this message.  To that end,
     * we need to know which keys this message is claiming to use. */
    if (context->context_priv->their_keyid == 0 ||
	    (sender_keyid != context->context_priv->their_keyid &&
		sender_keyid != context->context_priv->their_keyid - 1) ||
	    (recipient_keyid != context->context_priv->our_keyid &&
	     recipient_keyid != context->context_priv->our_keyid - 1) ||
	    sender_keyid == 0 || recipient_keyid == 0) {
	goto conflict;
    }

    if (sender_keyid == context->context_priv->their_keyid - 1 &&
	    context->context_priv->their_old_y == NULL) {
	goto conflict;
    }

    /* These are the session keys this message is claiming to use. */
    sess = &(context->context_priv->sesskeys
	    [context->context_priv->our_keyid - recipient_keyid]
	    [context->context_priv->their_keyid - sender_keyid]);

    gcry_md_reset(sess->rcvmac);
    gcry_md_write(sess->rcvmac, macstart, macend-macstart);
    if (otrl_mem_differ(givenmac, gcry_md_read(sess->rcvmac, GCRY_MD_SHA1),
	    20)) {
	/* The MACs didn't match! */
	goto conflict;
    }
    sess->rcvmacused = 1;

    /* Check to see that the counter is increasing; i.e. that this isn't
     * a replay. */
    if (otrl_dh_cmpctr(ctr, sess->rcvctr) <= 0) {
	goto conflict;
    }

    /* Decrypt the message */
    memmove(sess->rcvctr, ctr, 8);
    err = gcry_cipher_reset(sess->rcvenc);
    if (err) goto err;
    err = gcry_cipher_setctr(sess->rcvenc, sess->rcvctr, 16);
    if (err) goto err;
    err = gcry_cipher_decrypt(sess->rcvenc, data, datalen, NULL, 0);
    if (err) goto err;

    /* Save a copy of the current extra key */
    if (extrakey) {
	memmove(extrakey, sess->extrakey, OTRL_EXTRAKEY_BYTES);
    }

    /* See if either set of keys needs rotating */

    if (recipient_keyid == context->context_priv->our_keyid) {
	/* They're using our most recent key, so generate a new one */
	err = rotate_dh_keys(context);
	if (err) goto err;
    }

    if (sender_keyid == context->context_priv->their_keyid) {
	/* They've sent us a new public key */
	err = rotate_y_keys(context, sender_next_y);
	if (err) goto err;
    }

    gcry_mpi_release(sender_next_y);
    *plaintextp = (char *)data;

    /* See if there are TLVs */
    nul = data;
    while (nul < data+datalen && *nul) ++nul;
    /* If we stopped before the end, skip the NUL we stopped at */
    if (nul < data+datalen) ++nul;
    *tlvsp = otrl_tlv_parse(nul, (data+datalen)-nul);

    free(rawmsg);
    return gcry_error(GPG_ERR_NO_ERROR);

invval:
    err = gcry_error(GPG_ERR_INV_VALUE);
    goto err;
conflict:
    err = gcry_error(GPG_ERR_CONFLICT);
    goto err;
err:
    gcry_mpi_release(sender_next_y);
    free(data);
    free(rawmsg);
    return err;
}