예제 #1
0
/// Plexes the request depending on arguments.
int oterm_get_data(oterm_data *data, onion_request *req, onion_response *res){
  const char *username=onion_request_get_session(req,"username");
  if (!username){
    ONION_WARNING("Trying to enter authenticated area without username.");
    return OCS_FORBIDDEN;
  }
	oterm_session *o=(oterm_session*)onion_dict_get(data->sessions, onion_request_get_session(req,"username"));
	if (!o){
		o=oterm_session_new();
		onion_dict_lock_write(data->sessions);
		onion_dict_add(data->sessions,onion_request_get_session(req,"username"),o, 0);
		onion_dict_unlock(data->sessions);
	}
  const char *path=onion_request_get_path(req);

  ONION_DEBUG("Ask path %s (%p)", path, data);
  
  if (strcmp(path,"new")==0){
    if (onion_request_get_post(req, "command")){
      free(data->exec_command);
      data->exec_command=strdup(onion_request_get_post(req, "command"));
    }
    oterm_new(data, o, onion_request_get_session(req, "username"), onion_request_get_session(req, "nopam") ? 0 : 1 );
    return onion_shortcut_response("ok", 200, req, res);
  }
  if (strcmp(path,"status")==0)
    return oterm_status(o,req, res);

  return OCS_NOT_PROCESSED;
}
예제 #2
0
void t05_create_add_free_POST(){
	INIT_LOCAL();
	
	onion_request *req;
	int ok;
	
	req=onion_request_new(custom_io);
	FAIL_IF_EQUAL(req,NULL);
	FAIL_IF_NOT_EQUAL(req->connection.fd, -1);
	
	const char *query="POST /myurl%20/is/very/deeply/nested?test=test&query2=query%202&more_query=%20more%20query+10 HTTP/1.0\n"
													"Host: 127.0.0.1\n\rContent-Length: 50\n"
													"Other-Header: My header is very long and with spaces...\r\n\r\nempty_post=&post_data=1&post_data2=2&empty_post_2=\n";
	
	int i; // Straight write, with clean (keep alive like)
	for (i=0;i<10;i++){
		FAIL_IF_NOT_EQUAL(req->flags,0);
		ok=REQ_WRITE(req,query);
		
		FAIL_IF_NOT_EQUAL(ok, OCS_CLOSE_CONNECTION);
		FAIL_IF_EQUAL(req->flags,OR_GET|OR_HTTP11);
		
		FAIL_IF_EQUAL(req->headers, NULL);
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(req->headers,"Host"), "127.0.0.1");
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(req->headers,"Other-Header"), "My header is very long and with spaces...");

		FAIL_IF_NOT_EQUAL_STR(req->fullpath,"/myurl /is/very/deeply/nested");
		FAIL_IF_NOT_EQUAL_STR(req->path,"myurl /is/very/deeply/nested");

		FAIL_IF_EQUAL(req->GET,NULL);
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(req->GET,"test"), "test");
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(req->GET,"query2"), "query 2");
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(req->GET,"more_query"), " more query 10");

		const onion_dict *post=onion_request_get_post_dict(req);
		FAIL_IF_EQUAL(post,NULL);
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(post,"post_data"), "1");
		FAIL_IF_NOT_EQUAL_STR( onion_dict_get(post,"post_data2"), "2");
		FAIL_IF_NOT_EQUAL_STR( onion_request_get_post(req, "empty_post"), "");
		FAIL_IF_NOT_EQUAL_STR( onion_request_get_post(req, "empty_post_2"), "");

		onion_request_clean(req);
		FAIL_IF_NOT_EQUAL(req->GET,NULL);
	}
	
	onion_request_free(req);
	
	END_LOCAL();
}
예제 #3
0
onion_connection_status upload_file(upload_file_data *data, onion_request *req, onion_response *res){
	if (onion_request_get_flags(req)&OR_POST){
		const char *name=onion_request_get_post(req,"file");
		const char *filename=onion_request_get_file(req,"file");
		
		if (name && filename){
			char finalname[1024];
			snprintf(finalname,sizeof(finalname),"%s/%s",data->abspath,name);
			ONION_DEBUG("Copying from %s to %s",filename,finalname);

			unlink(finalname); // Just try to unlink it, if fail, sure its because it does not exist.
			int src=open(filename,O_RDONLY);
			int dst=open(finalname, O_WRONLY|O_CREAT, 0666);
			if (!src || !dst){
				ONION_ERROR("Could not open src or dst file (%d %d)",src,dst);
				return OCS_INTERNAL_ERROR;
			}
			ssize_t r,w;
			char buffer[1024*4];
			while ( (r=read(src,buffer,sizeof(buffer))) > 0){
				w=write(dst,buffer,r);
				if (w!=r){
					ONION_ERROR("Error writing file");
					break;
				}
			}
			close(src);
			close(dst);
		}
	}
	return 0; // I just ignore the request, but process over the FILE data
}
예제 #4
0
/// Resize the window. 
int oterm_resize(process *p, onion_request* req, onion_response *res){
	//const char *data=onion_request_get_query(req,"resize");
	//int ok=kill(o->pid, SIGWINCH);
	
	struct winsize winSize;
	memset(&winSize, 0, sizeof(winSize));
	const char *t=onion_request_get_post(req,"width");
	winSize.ws_row = (unsigned short)atoi(t ? t : "80");
	t=onion_request_get_post(req,"height");
	winSize.ws_col = (unsigned short)atoi(t ? t : "25");
	
	int ok=ioctl(p->fd, TIOCSWINSZ, (char *)&winSize) == 0;

	if (ok>=0)
		return onion_shortcut_response("OK",HTTP_OK, req, res);
	else
		return onion_shortcut_response("Error",HTTP_INTERNAL_ERROR, req, res);
}
예제 #5
0
/// Sets internally the window title, for reference.
int oterm_title(process *p, onion_request* req, onion_response *res){
	const char *t=onion_request_get_post(req, "title");
	
	if (!t)
		return onion_shortcut_response("Error, must set title", HTTP_INTERNAL_ERROR, req, res);
	
	if (p->title)
		free(p->title);
	p->title=strdup(t);
	
	ONION_DEBUG("Set term %d title %s", p->pid, p->title);
	
	return onion_shortcut_response("OK", HTTP_OK, req, res);
}
예제 #6
0
/// Input data to the process
int oterm_in(process *p, onion_request *req, onion_response *res){
	oterm_check_running(p);

	const char *data;
	data=onion_request_get_post(req,"type");
	ssize_t w;
	if (data){
		//fprintf(stderr,"%s:%d write %ld bytes\n",__FILE__,__LINE__,strlen(data));
		size_t r=strlen(data);
		w=write(p->fd, data, r);
		if (w!=r){
			ONION_WARNING("Error writing data to process. Not all data written. (%d).",w);
			return onion_shortcut_response("Error", HTTP_INTERNAL_ERROR, req, res);
		}
	}
	return onion_shortcut_response("OK", HTTP_OK, req, res);
}
예제 #7
0
파일: trivia.c 프로젝트: smipi1/onion
onion_connection_status check_answer(void *privdata, onion_request *req, onion_response *res)
{
	trivia_question const* const q = privdata;
	if (onion_request_get_flags(req)&OR_HEAD){
		onion_response_write_headers(res);
		return OCS_PROCESSED;
	}
	const char *answer = onion_request_get_post(req,"answer");
	const char *uri;
	if(strcmp_ignoring_case_and_whitespace(q->correct_answer, answer) == 0) {
		lamp->green = 65535;
		uri = q->correct_uri;
	} else {
		lamp->red = 65535;
		uri = q->again_uri;
	}
	return onion_shortcut_response_extra_headers("<h1>302 - Moved</h1>", HTTP_REDIRECT, req, res, "Location", uri, NULL );
}
예제 #8
0
/* return value marks, if reply string contains data which should
 * return to the web client:
 * -2: No data written into reply. Input generate error. Currently, it's not handled.
 * -1: No data written into reply. Input generate state which require reloading of web page.
 *  0: data written into reply
 *  1: No data written into reply, but input processed successful.*/
bool OnionServer::updateWebserver(
		Onion::Request *preq, int actionid, Onion::Response *pres ){
	VPRINT("Actionid: %i \n", actionid);
	switch(actionid){
		case 4:
			{ /* Command Message */
				const char* json_str = onion_request_get_post(preq->c_handler(), "cmd");
				std::string reply;

				if( json_str != NULL){
					Messages &q = m_b9CreatorSettings.m_queues;
					std::string cmd(json_str);
					q.add_command(cmd);	
					reply = "ok";
				}else{
					reply = "missing post variable 'cmd'";
				}

				pres->write(reply.c_str(), reply.size() );
				return true;
			}
			break;
		case 3:
			{ /* Quit */
				std::string reply("quit");
				pres->write(reply.c_str(), reply.size() );

				printf("Quitting...\n");
				m_b9CreatorSettings.lock();
				m_b9CreatorSettings.m_die = true;
				m_b9CreatorSettings.unlock();

				return true;
			}
			break;
		default:
			break;
	}

	return false;
}
예제 #9
0
파일: 08-post.c 프로젝트: Andrepuel/onion
onion_connection_status post_check(expected_post *post, onion_request *req){
	const char *filename=onion_request_get_post(req,"file");
	const char *tmpfilename=onion_request_get_file(req,"file");
	post->test_ok=1;
	ONION_DEBUG("Got filename %s, expected %s",filename,post->filename);
	if (strcmp(filename, post->filename)!=0){
		ONION_ERROR("File names do not match: %s %s", filename, post->filename);
		post->test_ok=0;
	}
	
	ONION_DEBUG("Temporal file %s",tmpfilename);
	
	struct stat st;
	if (stat(tmpfilename, &st)!=0){
		ONION_ERROR("Could not stat temp file");
		post->test_ok=0;
	}
	if (st.st_size!=post->size){
		ONION_ERROR("Size do not match, expected %d, got %d",post->size,st.st_size);
		post->test_ok=0;
	}

	char tmp[256];
	snprintf(tmp,sizeof(tmp),"%s-",tmpfilename);
	ONION_DEBUG("Linking to %s", tmp);
	
	FAIL_IF_NOT_EQUAL(link(tmpfilename, tmp),0);
	
	if (post->tmplink)
		free(post->tmplink);
	post->tmplink=strdup(tmp);

	if (post->tmpfilename)
		free(post->tmpfilename);
	post->tmpfilename=strdup(tmpfilename);

	return OCS_INTERNAL_ERROR;
}
예제 #10
0
int OnionServer::updateSetting(onion_request *req, onion_response *res){
	int actionid = atoi( onion_request_get_queryd(req,"actionid","0") );
	printf("Actionid: %i \n", actionid);
	switch(actionid){
		case 8:{  //quit programm
						 m_psettingKinectGrid->setMode(QUIT);
					 }
					 break;
		case 7:{  //load masks
						 m_psettingKinectGrid->setMode(LOAD_MASKS);
					 }
					 break;
		case 6:{ //save masks
						 m_psettingKinectGrid->setMode(SAVE_MASKS);
					 }
					 break;
		case 5:{ //select view
							//m_view = atoi( onion_request_get_queryd(req,"view","-1") );
							m_view = atoi( onion_request_get_post(req,"view") );
						 printf("Set view to %i.\n",m_view);
					 }
					 break;
		case 4:{ //repoke
						 printf("Repoke\n");
								m_psettingKinectGrid->setMode(REPOKE_DETECTION);
					 }
					 break;
		case 3:{ // area detection
							int start = atoi( onion_request_get_queryd(req,"start","1") );
							if( start == 1)
								m_psettingKinectGrid->setMode(AREA_DETECTION_START);
							else{
								m_psettingKinectGrid->setMode(AREA_DETECTION_END);
							}
					 }
			break;
		case 2:{
						 const char* filename = onion_request_get_post(req,"filename");
						 printf("Save new settingKinectGrid: %s\n",filename);
						 if( check_filename(filename ) == 1){
							 m_psettingKinect->saveConfigFile(filename);
							 m_psettingKinectGrid->setString("lastSetting",filename);
							 m_psettingKinectGrid->saveConfigFile("settingKinectGrid.ini");
						 }else{
						 	printf("Filename not allowed\n");
						 }
						 /* force reload of website */
						 return 0;
					 }
			break;
		case 1:{
						 const char* filename = onion_request_get_post(req,"filename");
						 printf("Load new settingKinectGrid: %s\n",filename);
						 if( check_filename(filename ) == 1){
							 m_psettingKinect->loadConfigFile(filename);
						 }else{
							 printf("Filename not allowed\n");
						 }
						 return -1;
					 }
			break;
		case 0:
		default:{
							printf("update settingKinect values\n");
							const char* json_str = onion_request_get_post(req,"settingKinect");
							if( json_str != NULL){
								//printf("Get new settingKinect: %s\n",json_str);
								m_psettingKinect->setConfig(json_str, NO);
							}else{
								return -1;
							}
						}
			break;
	}
	return 0; 
}
예제 #11
0
/**
 * @short Gets a put data
 * @memberof onion_request_t
 * @ingroup request
 */
const char *onion_request_get_put(onion_request *req, const char *query){
	return onion_request_get_post(req,query);
}
예제 #12
0
void
momf_canvasedit (struct mom_item_st *tkitm)
{
  enum canvedit_closoff_en
  {
    mec_wexitm,
    mec_protowebstate,
    mec_thiswebstate,
    mec__last
  };
  struct mom_item_st *wexitm = NULL;
  struct mom_item_st *thistatitm = NULL;
  struct mom_item_st *sessitm = NULL;
  struct mom_item_st *hsetitm = NULL;
  mom_item_lock (tkitm);
  const struct mom_boxnode_st *tknod =
    (struct mom_boxnode_st *) tkitm->itm_payload;
  MOM_DEBUGPRINTF (web, "momf_canvedit start tkitm=%s tknod=%s",
                   mom_item_cstring (tkitm),
                   mom_value_cstring ((const struct mom_hashedvalue_st *)
                                      tknod));
  if (mom_itype (tknod) != MOMITY_NODE || mom_raw_size (tknod) < mec__last)
    {
      /// should not happen
      MOM_WARNPRINTF ("momf_canvedit bad tknod %s",
                      mom_value_cstring ((const struct mom_hashedvalue_st *)
                                         tknod));
      goto end;
    }
  {
    wexitm = mom_dyncast_item (tknod->nod_sons[mec_wexitm]);
    assert (wexitm && wexitm->va_itype == MOMITY_ITEM);
    mom_item_lock (wexitm);
    thistatitm = mom_dyncast_item (tknod->nod_sons[mec_thiswebstate]);
    assert (thistatitm && thistatitm->va_itype == MOMITY_ITEM);
    MOM_DEBUGPRINTF (web,
                     "momf_canvedit tkitm=%s wexitm=%s paylkind %s thistatitm=%s",
                     mom_item_cstring (tkitm), mom_item_cstring (wexitm),
                     mom_itype_str (wexitm->itm_payload),
                     mom_item_cstring (thistatitm));
    mom_item_lock (thistatitm);
  }
  struct mom_webexch_st *wexch =
    (struct mom_webexch_st *) wexitm->itm_payload;
  assert (wexch && wexch->va_itype == MOMITY_WEBEXCH);
  sessitm = wexch->webx_sessitm;
  mom_item_lock (sessitm);
  MOM_DEBUGPRINTF (web,
                   "momf_canvedit tkitm=%s wexch #%ld meth %s fupath %s sessitm %s",
                   mom_item_cstring (tkitm), wexch->webx_count,
                   mom_webmethod_name (wexch->webx_meth),
                   onion_request_get_fullpath (wexch->webx_requ),
                   mom_item_cstring (sessitm));
  sessitm->itm_pcomp = mom_vectvaldata_resize (sessitm->itm_pcomp, mes__last);
  if (!
      (hsetitm =
       mom_dyncast_item (mom_vectvaldata_nth
                         (sessitm->itm_pcomp, mes_itmhset))))
    {
      hsetitm = mom_clone_item (MOM_PREDEFITM (hashset));
      mom_vectvaldata_put_nth (sessitm->itm_pcomp, mes_itmhset,
                               (struct mom_hashedvalue_st *) hsetitm);
      hsetitm->itm_payload =
        (struct mom_anyvalue_st *) mom_hashset_reserve (NULL, 20);
      MOM_DEBUGPRINTF (web,
                       "momf_canvedit tkitm=%s wexch #%ld new hsetitm %s",
                       mom_item_cstring (tkitm), wexch->webx_count,
                       mom_item_cstring (hsetitm));
    }
  else
    MOM_DEBUGPRINTF (web,
                     "momf_canvedit tkitm=%s wexch #%ld got hsetitm %s",
                     mom_item_cstring (tkitm), wexch->webx_count,
                     mom_item_cstring (hsetitm));

  mom_item_lock (hsetitm);
  if (wexch->webx_meth == MOMWEBM_POST)
    {
      const char *dofillcanvas =
        onion_request_get_post (wexch->webx_requ, "do_fillcanvas");
      MOM_DEBUGPRINTF (web,
                       "momf_canvedit tkitm=%s wexch #%ld do_fillcanvas %s",
                       mom_item_cstring (tkitm), wexch->webx_count,
                       dofillcanvas);
      if (dofillcanvas)
        dofillcanvas_canvedit_mom (wexch, tkitm, wexitm, thistatitm);
    }
end:
  if (hsetitm)
    mom_item_unlock (hsetitm);
  if (thistatitm)
    mom_item_unlock (thistatitm);
  if (wexitm)
    mom_item_unlock (wexitm);
  if (sessitm)
    mom_item_unlock (sessitm);
  mom_item_unlock (tkitm);
}                               /* end of momf_canvedit */
예제 #13
0
/* return value marks, if reply string contains data which should
 * return to the web client:
 * -3: No data written into reply. Input generate state which require reloading of web page.
 * -2: No data written into reply, but input processed successful.
 * -1: No data written into reply. Input has generate error. (Currently not used.)
 *  0: data written into reply
 *  1: data written, but error occours.
 *  2: two signals wrote data. The output is likely unuseable
 
Old approach was:
Signal return value is OR-Combination of signal handles.
    true: request handling succesful. 
		false: no handler deals with the request.
 */
int OnionServer::updateWebserver(
		Onion::Request *preq, int actionid, Onion::Response *pres ){
	VPRINT("Http action id: %i \n", actionid);

	switch(actionid){
		case HTTP_ACTION_SEND_COMMAND:
			{ /* Command Message, unused */
				const char* json_str = onion_request_get_post(preq->c_handler(), "cmd");
				std::string reply;

				if( json_str != NULL){
					Messages &q = m_settingKinect.m_queues;
					std::string cmd(json_str);
					q.add_command(cmd);	
					reply = "ok";
				}else{
					reply = "missing post variable 'cmd'";
				}

				pres->write(reply.c_str(), reply.size() );
				return 0;
			}
			break;

		case HTTP_ACTION_RESET_CONFIG:{  //reset config values to defaults.
						 m_settingKinect.loadConfigFile("");
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_QUIT_PROGRAM:{  //quit programm
						 std::string reply("quit");
						 pres->write(reply.c_str(), reply.size() );
						 VPRINT("Quitting...\n");
						 m_settingKinect.lock();
						 //m_settingKinect.m_die = true;
						 m_settingKinect.setMode(QUIT);
						 m_settingKinect.unlock();
						 return 0;
					 }
					 break;
		case HTTP_ACTION_LOAD_MASKS:{  //load masks
						 m_settingKinect.setMode(LOAD_MASKS);
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_SAVE_MASKS:{ //save masks
						 m_settingKinect.setMode(SAVE_MASKS);
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_SELECT_VIEW:{ //select view
						 //m_view = atoi( onion_request_get_queryd(req,"view","0") );
						 m_view = (View) atoi( onion_request_get_post(preq->c_handler(),"view") );
						 VPRINT("Set view to %i.\n",(int)m_view);
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_REPOKE:{ //repoke
						 VPRINT("Repoke\n");
						 m_settingKinect.setMode(REPOKE_DETECTION);
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_SET_AREA_DETECTION:{ // area detection
						 int start = atoi( onion_request_get_post(preq->c_handler(),"start") );
						 if( start == 1)
							 m_settingKinect.setMode(AREA_DETECTION_START);
						 else{
							 m_settingKinect.setMode(AREA_DETECTION_END);
						 }
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_SAVE_CONFIG:{
						 const char* configFilename = onion_request_get_post(preq->c_handler(), "configFilename");
						 VPRINT("Save new settingKinectGrid: %s\n",configFilename);
						 if( check_configFilename(configFilename) ){
							 m_settingKinect.saveConfigFile(configFilename);
							 //m_settingKinect.setString("lastSetting",configFilename);
							 //m_settingKinect.saveConfigFile("settingKinectGrid.ini");
						 }else{
							 VPRINT("Filename not allowed\n");
						 }
						 std::string reply("ok");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
		case HTTP_ACTION_LOAD_CONFIG:{
						 const char* configFilename = onion_request_get_post(preq->c_handler(), "configFilename");
						 VPRINT("Load new settingKinectGrid: %s\n",configFilename);
						 if( check_configFilename(configFilename) ){
							 m_settingKinect.loadConfigFile(configFilename);
						 }else{
							 printf("Filename not allowed\n");
						 }
						 /* force reload of website */
						 std::string reply("reload");
						 pres->write(reply.c_str(), reply.size() );
						 return 0;
					 }
					 break;
					 //case 0: // this case will be handled in webserverUpdateConfig
		default:{
						}
						break;
	}

	return -3;
}
예제 #14
0
파일: api_mail.c 프로젝트: bmybbs/api
static int api_mail_do_post(ONION_FUNC_PROTO_STR, int mode)
{
	const char * userid = onion_request_get_query(req, "userid");
	const char * appkey = onion_request_get_query(req, "appkey");
	const char * sessid = onion_request_get_query(req, "sessid");
	const char * token = onion_request_get_query(req, "token");
	const char * to_userid = onion_request_get_query(req, "to_userid");
	const char * title = onion_request_get_query(req, "title");
	const char * backup = onion_request_get_query(req, "backup");

	if(!userid || !appkey || !sessid || !title || !to_userid || !token)
		return api_error(p, req, res, API_RT_WRONGPARAM);

	struct userec *ue = getuser(userid);
	if(!ue)
		return api_error(p, req, res, API_RT_NOSUCHUSER);

	struct userec currentuser;
	memcpy(&currentuser, ue, sizeof(currentuser));
	free(ue);

	int r = check_user_session(&currentuser, sessid, appkey);
	if(r != API_RT_SUCCESSFUL) {
		return api_error(p, req, res, r);
	}

	if(HAS_PERM(PERM_DENYMAIL)) {
		return api_error(p, req, res, API_RT_MAILNOPPERM);
	}

	int uent_index = get_user_utmp_index(sessid);
	struct user_info *ui = &(shm_utmp->uinfo[uent_index]);
	if(strcmp(ui->token, token) != 0) {
		return api_error(p, req, res, API_RT_WRONGTOKEN);
	}

	// 更新 token 和来源 IP
	getrandomstr_r(ui->token, TOKENLENGTH+1);
	const char * fromhost = onion_request_get_header(req, "X-Real-IP");
	memset(ui->from, 0, 20);
	strncpy(ui->from, fromhost, 20);

	if(check_user_maxmail(currentuser)) {
		return api_error(p, req, res, API_RT_MAILFULL);
	}

	struct userec *to_user = getuser(to_userid);
	if(!to_user) {
		return api_error(p, req, res, API_RT_NOSUCHUSER);
	}

	if(inoverride(currentuser.userid, to_user->userid, "rejects")) {
		free(to_user);
		return api_error(p, req, res, API_RT_INUSERBLIST);
	}

	const char * data = onion_request_get_post(req, "content");

	char filename[80];
	sprintf(filename, "bbstmpfs/tmp/%s_%s.tmp", currentuser.userid, ui->token);

	char * data2 = strdup(data);
	while(strstr(data2, "[ESC]") != NULL)
		data2 = string_replace(data2, "[ESC]", "\033");

	char * data_gbk = (char *)malloc(strlen(data2)*2);
	u2g(data2, strlen(data2), data_gbk, strlen(data2)*2);

	f_write(filename, data_gbk);
	free(data2);

	int mark=0;		// 文件标记
	//if(insertattachments(filename, data_gbk, currentuser->userid)>0)
		//mark |= FH_ATTACHED;

	free(data_gbk);

	char * title_tmp = (char *)malloc(strlen(title)*2);
	u2g(title, strlen(title), title_tmp, strlen(title)*2);
	char title_gbk[80], title_tmp2[80];
	strncpy(title_gbk, title_tmp[0]==0 ? "No Subject" : title_tmp, 80);
	snprintf(title_tmp2, 80, "{%s} %s", to_user->userid, title);
	free(title_tmp);

	r = do_mail_post(to_user->userid, title, filename, currentuser.userid,
			currentuser.username, fromhost, 0, mark);
	if(backup && strcasecmp(backup, "true")==0) {
		do_mail_post_to_sent_box(currentuser.userid, title_tmp2, filename, currentuser.userid,
			currentuser.username, fromhost, 0, mark);
	}

	unlink(filename);
	free(to_user);

	if(r<0) {
		return api_error(p, req, res, API_RT_MAILINNERR);
	}

	api_set_json_header(res);
	onion_response_printf(res, "{ \"errcode\":0, \"token\":\"%s\" }", ui->token);

	return OCS_PROCESSED;
}