/* This file is part of Bolixo. Bolixo is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bolixo 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Bolixo. If not, see . */ #include #include #include #include #include #include #include #include #include #define DEFINE_TBFTYPE #include "../bolixo.h" #define DEFINE_USERINFO #include "util.h" #include #include "../bolixo.m" #include #define INSTRUMENT_EXTERN #include "../instrument.h" #include "w_var.h" #include "steps.h" using namespace std; USERLOGINFO userinfo; string w_session; #define bo_sessiond_client_test_NOTNEED #define bo_sessiond_client_getsessioninfovars_NOTNEED #define bo_sessiond_client_getsessioninfovars_v2_NOTNEED #define bo_sessiond_client_waitevent_NOTNEED #define bo_sessiond_client_setnotify_NOTNEED #define bo_sessiond_client_ping_NOTNEED #define bod_client_createsession_NOTNEED #define bod_client_login_NOTNEED #define bod_client_logout_NOTNEED #define bod_client_adduser_NOTNEED #define bod_client_confirmuser_NOTNEED #define bod_client_deleteuser_NOTNEED #define bod_client_confirmdelete_NOTNEED #define bod_client_addfile_NOTNEED #define bod_client_addfile_bob_NOTNEED #define bod_client_appendfile_NOTNEED #define bod_client_delfile_NOTNEED #define bod_client_undelete_NOTNEED #define bod_client_modifyfile_NOTNEED #define bod_client_modifyfile_bob_NOTNEED #define bod_client_rename_NOTNEED #define bod_client_copy_NOTNEED #define bod_client_readfile_NOTNEED #define bod_client_mkdir_NOTNEED #define bod_client_rmdir_NOTNEED #define bod_client_listdir_NOTNEED #define bod_client_set_access_NOTNEED #define bod_client_create_group_list_NOTNEED #define bod_client_create_group_NOTNEED #define bod_client_set_group_NOTNEED #define bod_client_set_member_NOTNEED #define bod_client_delete_list_NOTNEED #define bod_client_delete_group_NOTNEED #define bod_client_list_lists_NOTNEED #define bod_client_list_groups_NOTNEED #define bod_client_list_inboxes_NOTNEED #define bod_client_list_msgs_NOTNEED #define bod_client_sendmsg_NOTNEED #define bod_client_sendmsg_project_NOTNEED #define bod_client_replymsg_NOTNEED #define bod_client_replymsg_project_NOTNEED #define bod_client_sendattach_NOTNEED #define bod_client_create_project_dir_NOTNEED #define bod_client_verifysign_NOTNEED #define bod_client_sendtalk_NOTNEED #define bod_client_list_talk_NOTNEED #define bod_client_contact_list_NOTNEED #define bod_client_public_checkuser_NOTNEED #define bod_client_public_listdir_NOTNEED #define bod_client_public_list_talk_NOTNEED #define bod_client_config_write_NOTNEED #define bod_client_config_read_NOTNEED #define bod_client_contact_manage_NOTNEED #define bod_client_contact_request_NOTNEED #define bod_client_sendtalk_file_NOTNEED #define bod_client_set_group_desc_NOTNEED #define bod_client_set_list_desc_NOTNEED #define bod_client_form_readvar_NOTNEED #define bod_client_form_savevar_NOTNEED #define bod_client_form_deletevar_NOTNEED #define bod_client_form_deleteall_NOTNEED #define bod_client_interest_set_NOTNEED #define bod_client_interest_unset_NOTNEED #define bod_client_interest_list_NOTNEED #define bod_client_interest_check_NOTNEED #define bod_client_info_read_NOTNEED #define bod_client_info_write_NOTNEED #define bod_client_systempubkey_NOTNEED #define bod_client_systemsign_NOTNEED #define bod_client_registernode_NOTNEED #define bod_client_getpubkey_NOTNEED #define bod_client_remotelogin_NOTNEED #define bod_client_remotepass_NOTNEED #define bod_client_nodelogin_NOTNEED #define bod_client_nodepass_NOTNEED #define bod_client_remote_interest_set_NOTNEED #define bod_client_remote_interest_unset_NOTNEED #define bod_client_sendtalk_anon_NOTNEED #define bod_client_list_contacts_NOTNEED #define bod_client_get_notification_NOTNEED #define bod_client_set_notification_NOTNEED #define bod_client_playstep_NOTNEED #define bod_client_playstep_more_NOTNEED #define bod_client_set_members_NOTNEED #define bod_client_contact_remove_NOTNEED #define bod_client_waitevent_NOTNEED #define bod_client_list_members_NOTNEED #include "../proto/bod_client.protoch" #define bo_sessiond_client_getsessioninfo_NOTNEED #define bo_sessiond_client_setvar_NOTNEED #include "../proto/bo-sessiond_client.protoch" W_UNSIGNED w_robot ("robot"); W_UNSIGNED w_showmsg ("showmsg"); W_SSTRING w_file("file"); static float small_size=0.9; static unsigned draw_tab_height() { unsigned fontsize = tlmpweb_fontsize(); unsigned height = fontsize*1.3*small_size; if (height < 20) height = 20; return height+2; // Keep some space at the top } unsigned draw_tab ( const char *id_suffix, // id suffix to assemble the ID of the SVG path unsigned width, // TAB width or 0 (computed from the title) const char *fill, // Fill color const char *fill_in, // Fill color for mouseover bool close, // Close the path or not const char *title, bool drawx, // Put an X at the end (normally to close the TAB) PARAM_STRING href, // URL when the TAB is clicked PARAM_STRING xref) // URL when the X is clicked { unsigned height = draw_tab_height(); static STATIC_ID alloc_id; unsigned idnum = alloc_id++; /- if (width == 0){ width = tlmpweb_displaylen(title,small_size)+5; // See the .small css entry above // +5 because the server fonts do not match perfectly if (drawx){ width += 10+5+15; }else{ // We put 10 pixels on each side width += 10+10; } } htmlprintf ("\n",width,height); htmlprintf ("\n"); if (drawx && !tlmpweb_ismobile()){ unsigned x=width-17; htmlprintf ("\n",xref.ptr,idnum,idnum); // First rect is there to make the a tag larger. fill=none does not work. So we have to give it the same color // as the tab and changefill2 will change the color of the tab and this rect. // The second rect is used to highlit the X htmlprintf ("\n",ids.c_str(),x-5,4,height-6,fill); unsigned hmiddle = height/2+2; unsigned h14 = height-8; unsigned h8=height/3; if (h8 & 1) h8++; // Make sure h8 is even. htmlprintf ("\n",idnum,x-3,4,h14,height-4); htmlprintf ("\n" ,x,hmiddle-h8/2,h8,h8 ,x,hmiddle+h8/2,h8,h8); htmlout ("\n"); } htmlout ("\n"); return width; } // Same thing with no X unsigned draw_tab (const char *id_suffix, unsigned width, const char *fill, const char *fill_in, bool close, const char *title, PARAM_STRING href) { return draw_tab (id_suffix,width,fill,fill_in,close,title,false,href,""); } void draw_left_arrow(bool visible, PARAM_STRING xref) { unsigned height = draw_tab_height(); htmlprintf ("\n",height); if (visible){ static STATIC_ID alloc_id; unsigned id=alloc_id++; htmlprintf ("\n",xref.ptr,id,id); htmlprintf ("\n",id,height-4); } const char *color = visible ? "black" : "white"; unsigned h2=height/2; htmlprintf ("\n",color,color,h2,h2-8,h2+8); if (visible) htmlout ("\n"); // Always put the underline htmlprintf ("\n",height); htmlout ("\n"); } void draw_right_arrow(bool visible, PARAM_STRING xref) { unsigned height = draw_tab_height(); htmlprintf ("\n",height); if (visible){ static STATIC_ID alloc_id; unsigned id=alloc_id++; htmlprintf ("\n",xref.ptr,id,id); htmlprintf ("\n",id,height-4); } const char *color = visible ? "black" : "white"; unsigned h2=height/2; htmlprintf ("\n",color,color,h2,h2-8,h2+8); if (visible) htmlout ("\n"); // Always put the underline htmlprintf("\n",height); htmlout ("\n"); } static void print_href_c (const char *id_suffix, const char *title, PARAM_STRING href, const char *color, bool close) { draw_tab (id_suffix,0,color,color,close,title,href); } void print_href (const char *id_suffix,const char *title, PARAM_STRING href, bool notify) { print_href_c(id_suffix,title, href,notify ? "orange" : "#EAEAEA",true); } void print_aref (const char *id_suffix, const char *page, const char *title, int step, bool notify) { string href = string_f ("%s?webstep=%d",page,step); print_href (id_suffix,title,href,notify); } void print_aref (const char *id_suffix,const char *title, int step, bool notify) { print_aref (id_suffix,tlmpweb_curpage(),title,step,notify); } void print_aref_selected (const char *id_suffix, const char *page, const char *title, int step, bool notify) { string href = string_f ("%s?webstep=%d",page,step); print_href_c (id_suffix,title,href,notify ? "orange" : "white", false); } void print_aref_selected (const char *id_suffix, const char *title, int step, bool notify) { print_aref_selected (id_suffix,tlmpweb_curpage(),title,step,notify); } const char *format_line (const char *s) { while (*s != '\0' && *s != '\n'){ char car = *s++; if (car == '<'){ htmlout ("<"); }else if (car == '>'){ htmlout (">"); }else{ htmlout (car); } } return s; } const char *format_url (const char *s) { if (strncmp(s,"http://",7)!=0 && strncasecmp(s,"https://",8)!=0) htmlout ("http://"); return format_line (s); } void format_href(const char *s) { htmlout (""); if (strlen(s) > 50){ string tmp = string(s,80) + "..."; format_url(tmp.c_str()); }else{ format_url(s); } htmlout(""); } void format_content (const char *s, int nbline, bool &more) { more = false; bool ol_on = false; bool ul_on = false; bool quote_on = false; int noline = 0; while (*s != '\0' && noline < nbline){ if (*s == '\n'){ if (ul_on){ htmlout (""); ul_on = false; }else if (ol_on){ htmlout (""); ol_on = false; }else if (quote_on){ htmlout (""); quote_on = false; } htmlout ("

\n"); s++; noline++; }else{ const char *closing = ""; if (*s == '*'){ if (ol_on){ ol_on = false; htmlout (""); } if (!ul_on){ htmlout ("

    "); ul_on = true; } htmlout ("
  • "); s++; }else if (*s == '#'){ if (ul_on){ htmlout ("
"); ul_on = false; } if (!ol_on){ htmlout ("
    "); ol_on = true; } htmlout ("
  1. "); s++; }else if (*s == '?'){ s++; while (*s == ' ') s++; htmlout (""); s = format_url (s); htmlout (""); }else if (*s == '!'){ s++; if (*s == '!'){ s++; htmlout ("

    "); closing = "

    "; }else{ htmlout (""); closing = ""; } }else if (*s == '>'){ quote_on=true; s++; htmlout ("
    "); } s = format_line (s); noline++; htmlout (closing); if (*s == '\n'){ s++; htmlout ('\n'); } } } if (ul_on){ htmlout (""); } if (ol_on){ htmlout ("
"); } if (*s != '\0') more = true; } void format_content (const char *s) { bool more; format_content(s,10000,more); } void formatting_tips() { printhref ("/marker.html","Formatting tips",false); } void util_google_code() { #if 0 htmlout ("\n"); htmlout ("\n"); #endif } static unsigned mobile_body_font_size=300; static unsigned mobile_input_font_size=50; static unsigned mobile_button_font_size=80; void util_setmobilespecs (unsigned body_font_size, unsigned input_font_size, unsigned button_font_size) { mobile_body_font_size = body_font_size; mobile_input_font_size = input_font_size; mobile_button_font_size = button_font_size; } /* The experimental flag is set in the web container in /etc/bolixonode.conf. It is on in the development version. This allows for development of long and complex features, kept invisibles from users until they are ready. */ static bool experimental=false; void util_set_experimental() { experimental = true; } bool util_experimental() { return experimental; } /* Javascript needed at the end of the HTML */ void util_endscript(PARAM_STRING urlparam) { // Script to make sure the webtabs tabs use the full height of the screen // Then it takes the webtables and grows them too. var inside_onread = false; document.onreadystatechange = function () { if (!inside_onread){ inside_onread = true; var state = document.readyState; if (state == 'interactive') { fixsize(); } else if (state == 'complete') { /- tlmpweb_position_popup_js(); } inside_onread = false; } }; var diff = 0; function allDescendants (node,total) { //console.log ('Enter ' + node.className + ' ' + node.id); if (node.className == "tabs" || node.className == "subtabs"){ var bord=(node.offsetHeight-node.clientHeight); //var style = node.currentStyle || window.getComputedStyle(node); var style = window.getComputedStyle(node); var margins = parseInt(style.marginTop,10) + parseInt(style.marginBottom,10); var paddings = parseInt(style.paddingTop,10) + parseInt(style.paddingBottom,10); total += bord + margins + paddings; console.log ("border cls=" + node.className + " node.id=" + node.id + " total="+total+" bord="+bord + " margins="+margins+ " paddings="+paddings); if (node.className == "subtabs"){ console.log ("Set subtabs "+node.id+" total="+total+" oldheight="+node.offsetHeight); node.style.height = diff - total; } } var last_webtable = null; for (var i = 0; i < node.childNodes.length; i++) { var child = node.childNodes[i]; if (child.id != null && (child.id == "tabs" || child.id == "tab_form" || child.id == "webtable-top")){ var add = child.offsetHeight; var style = window.getComputedStyle(child); add += parseInt(style.getPropertyValue('margin-top'),10); add += parseInt(style.getPropertyValue('margin-bottom'),10); var border_width = style.getPropertyValue('border-top-width'); if (child.id == "tab_form") add += 2; // Bug scrollbar ??? console.log ("child.id="+child.id+" add="+add+" border-width="+border_width); total += add; if (last_webtable != null){ console.log ("last_webtable "+last_webtable.id+"="+add); console.log ("last Set webtable "+last_webtable.id+" total="+total+" oldheight="+last_webtable.offsetHeight); last_webtable.style.height = diff - total; } console.log ("child.id=" + child.id + " total="+total+" height="+child.offsetHeight); }else{ if (child.id != null && child.id == "vframe2h"){ var bord=(child.offsetWidth-child.clientWidth); total += bord; console.log ("border child.id=" + child.id + " total="+total+" border="+bord); total = allDescendants(child,total); }else if (child.className == "webtable" || child.className == "textgrow"){ console.log ("Set webtable "+child.id+" total="+total+" oldheight="+child.offsetHeight); child.style.height = diff - total; last_webtable = child; //break; }else{ total = allDescendants(child,total); } } } return total; } function fixsize(){ document.getElementById('main').style.height = window.innerHeight-1; var t = document.getElementById('head').offsetHeight; diff = window.innerHeight-t-3; console.log ("diff="+diff+" offsetHeight="+t); document.getElementById('body').style.height = diff-1; var tabs = document.getElementsByClassName("tabs"); for (var i=0; i webtable_setscroll(); if (!tlmpweb_isrobot() && tlmpweb_some_geometry_missing()){ if (tlmpweb_is_repost()){ tlmp_error ("util_endscript geometry_missing repost=1"); }else if (0){ tlmp_warning ("some geometry missing"); }else{ const char *sep1 = "?"; const char *sep2 = "&"; if (urlparam.ptr[0] == '\0'){ sep1 = sep2 = ""; } //tlmp_warning ("geometry_missing formsubmit, doing repost\n"); htmlprintf ("formsubmit(\"%s%s%s&repost=1\");\n",tlmpweb_curpage(),sep1,urlparam.ptr); } } ?> } void util_meta() { htmlout ("\n"); } void util_defstyles() { unsigned body_font_size=85; unsigned input_font_size=100; unsigned button_font_size=90; unsigned input_checkbox=1; bool ismobile = tlmpweb_ismobile(); if (ismobile){ body_font_size = mobile_body_font_size; input_font_size = mobile_input_font_size; button_font_size = mobile_button_font_size; small_size = 1.0; input_checkbox = 3; } htmlout ("\n"); tlmpweb_setscripts(); htmlout ("\n"); } /* Draw the dot menu (3 bar in fact :-) ) only. No action or script attached. */ void util_draw_dotmenu(const char *id_menu, bool is_active, bool notify) { unsigned height = draw_tab_height(); unsigned width = height; unsigned thick = 2; unsigned line_y = height/4; unsigned skip_y = height/4; unsigned line_x1 = 5; unsigned line_x2 = width-5; if (tlmpweb_ismobile()){ width *= 2; thick = 4; line_x1 = 20; line_x2 = width-20; } htmlprintf ("\n",width,height); htmlprintf ("\n",id_menu,width,height,notify ? "orange" : "white"); for (int i=0; i<3; i++){ htmlprintf ("\n" ,line_x1,line_y,line_x2,line_y,thick); line_y += skip_y; } htmlprintf ("",is_active ? "white" : "black",height,width); htmlout (""); } /* Draw a 3 dots menu and the associated popup. */ void util_dotmenu (const vector &menu, bool is_active, bool notify) { static const char *id_dropdown = "dropdown-dot"; static const char *id_menu = "dotmenu"; unsigned popup_width = 0; { DIV d("dropdown"); d.print(); DIV dd("dropdown-content",id_dropdown); dd.print(); for (auto &m:menu){ htmlprintf ("%s\n",tlmpweb_curpage(),m.step,m.menu); unsigned menu_width = tlmpweb_displaylen(m.menu)+20; if (menu_width > popup_width) popup_width = menu_width; } } DIV d; d.id(id_menu).print(); htmlprintf ("\n",id_dropdown,id_menu,popup_width); util_draw_dotmenu (id_menu,is_active,notify); htmlout ("\n"); } void printhref(const char *url, const char *text) { htmlprintf ("
 %s 
",url,text); } void printhref_selected(const char *url, const char *text) { htmlprintf ("
 %s 
",url,text); } void printhref(const char *url, const char *text, bool largewindow) { if (w_robot){ htmlprintf ("%s",url,text); }else{ const char *script = largewindow ? "popup_large" : "popup_small"; htmlprintf ("
 %s 
",script,url,text); } } void printhref_raw(const char *url, const char *text, bool largewindow) { if (w_robot){ htmlprintf ("%s",url,text); }else{ const char *script = largewindow ? "popup_large" : "popup_small"; htmlprintf ("%s",script,url,text); } } string format_date (unsigned format, PARAM_STRING pdate) { static TRANS_NOTLOAD *tbmonth[]={P_MSG_U(I_JANUARY,"Jan"),P_MSG_U(I_FEBRUARY,"Feb"),P_MSG_U(I_MARCH,"Mar") ,P_MSG_U(I_APRIL,"Apr"),P_MSG_U(I_MAY,"May"),P_MSG_U(I_JUNE,"Jun") ,P_MSG_U(I_JULY,"Jul"),P_MSG_U(I_AUGUST,"Aug"),P_MSG_U(I_SEPTEMBER,"Sep") ,P_MSG_U(I_OCTOBER,"Oct"),P_MSG_U(I_NOVEMBER,"Nov"),P_MSG_U(I_DECEMBER,"Dec")}; const char *date = pdate.ptr; if (format != 0){ return date; }else if (strlen(date)==19){ int year = atoi(date); int month = atoi(date+5); int day = atoi(date+8); int hour = atoi(date+11); int minu = atoi(date+14); const char *ampm = "AM"; if (hour > 12){ hour -= 12; ampm="PM"; } return string_f (" %s %d %04d @%d:%02d%s",tbmonth[month-1]->get(),day,year,hour,minu,ampm); }else if (strlen(date)==10){ int year = atoi(date); int month = atoi(date+5); int day = atoi(date+8); return string_f (" %s %d %04d",tbmonth[month-1]->get(),day,year); }else{ return date; } } string format_time (unsigned format, PARAM_STRING ptime) { const char *time = ptime.ptr; if (format != 0){ return string(time,5); }else if (strlen(time)==8){ int hour = atoi(time); int minu = atoi(time+3); const char *ampm = "AM"; if (hour > 12){ hour -= 12; ampm="PM"; } return string_f ("%d:%02d%s",hour,minu,ampm); }else{ return time; } } void util_formanchor() { htmlprintf ("\n"); } void button_row(_F_button_row &c, int border, const char *bgcolor, bool alignleft) { vector left_lines; tlmpweb_pushgrab(left_lines); c.align="left"; c.draw(); tlmpweb_popgrab(); vector right_lines; tlmpweb_pushgrab(right_lines); c.align="right"; c.spliton = false; c.draw_right(); tlmpweb_popgrab(); if (left_lines.size() > 0 || right_lines.size()>0){ DIV cols; cols.dispflex().flowrow(); if (!alignleft) cols.sfloat("right"); cols.print(); if (c.href_arrow_left.size() > 0){ DIV col; col.flexfixe().print(); draw_left_arrow(c.arrow_left_visible,c.href_arrow_left); } { DIV col; col.flexgrow().overflow("hidden").print(); DIV d; d.dispflex().flowrow().bg("none").overflow("hidden").print(); if (left_lines.size() > 0){ htmlout (left_lines); htmlout ("\n"); } if (c.endline){ htmlout ("
\n"); unsigned height = draw_tab_height(); htmlprintf ("\n",c.endline_width,height); htmlprintf ("\n",height,c.endline_width); htmlout ("\n"); htmlout ("
\n"); } } if (c.href_arrow_right.size() > 0){ DIV col; col.flexfixe().print(); draw_right_arrow(c.arrow_right_visible,c.href_arrow_right); } DIV end; end.flexfixe().print(); if (right_lines.size() > 0){ htmlout (right_lines); htmlout ("\n"); } } } void button_row(_F_button_row &c, int border, const char *bgcolor) { button_row(c,border,bgcolor,true); } void button_row(_F_button_row &c, int border) { button_row (c,border,"white",true); } void button_row(_F_button_row &c) { button_row(c,0,"white",true); } void _F_button_row::draw_right() { } void _F_button_row::reset() { spliton = false; tlmpweb_resetgrab(); } void _F_button_row::split() { if (spliton) htmlout ("\n"); htmlout ("
\n"); spliton = true; } void _F_button_row::drawendline(unsigned width) { endline_width = width; endline = true; } void _F_button_row::drawleftarrow (PARAM_STRING href, bool visible) { arrow_left_visible = visible; href_arrow_left = href.ptr; } void _F_button_row::drawrightarrow (PARAM_STRING href, bool visible) { arrow_right_visible = visible; href_arrow_right = href.ptr; } static const char *tbhttptype[]={ "application/octet-stream", //FILE_UNKNOWN "text/html", //FILE_TEXT "audio/mp3", //FILE_SOUND_MP3 "audio/ogg", //FILE_SOUND_OGG "image/jpeg", //FILE_IMAGE_JPG "image/png", //FILE_IMAGE_PNG "image/gig", //FILE_IMAGE_GIF "video/mpeg", //FILE_VIDEO "application/octet-stream", //FILE_DOC_SUDOKU "application/octet-stream", //FILE_DOC_CHECKER "application/octet-stream", //FILE_DOC_CHESS "application/octet-stream", //FILE_DOC_TICTACTO "application/octet-stream", //FILE_ZIP "application/octet-stream", //FILE_TGZ "application/octet-stream", //FILE_DOC_WORDPROC "application/octet-stream", //FILE_DOC_WHITEBOARD "video/webm", //FILE_WEBM "image/tiff", //FILE_IMAGE_TIFF "application/pdf", //FILE_PDF "application/octet-stream", //FILE_DOC_CALC "application/octet-stream", //FILE_DOC_PHOTOS "application/octet-stream", //FILE_DOC_VIDCONF "audio/x-wav", // FILE_SOUND_WAV }; static_assert(sizeof(tbhttptype)/sizeof(tbhttptype[0])==FILE_TYPE_LAST,"tbhttptype is incomplete"); static void util_sendfile_common(CONNECT_INFO &con, const READINFO_receive &info, const BOB_TYPE &content, bool more, const char *handle, const char *session) { glocal CONNECT_INFO *con = &con; tlmpweb_setmodified(info.modified); tlmpweb_setexpire(time(NULL)+365*24*60*60); // Expires in one year tlmpweb_doctype (tbhttptype[info.file_type],info.size); htmlwrite (content.getbuffer(),content.getsize()); glocal bool more = more; while (glocal.more){ (*glocal.con,session,handle); htmlwrite (content.getbuffer(),content.getsize()); glocal.more = more; //tlmp_error ("readmore success=%d msg=%s\n",success,msg); } } static int util_sendfile (const char *fname) { int ret = -1; FILE *fin = fopen (fname,"r"); if (fin != NULL){ struct stat64 st; time_t mod = 0; unsigned size = 0; if (fstat64(fileno(fin),&st)!=-1){ size = st.st_size; mod = st.st_mtime; } tlmpweb_setmodified(mod); tlmpweb_doctype ("image/jpeg",size); char buf[64*1024]; int n; while ((n=fread(buf,1,sizeof(buf),fin))>0){ htmlwrite(buf,n); } fclose (fin); ret = 0; } return ret; } int util_sendfile (CONNECT_INFO &con, PARAM_STRING session, PARAM_STRING filename) { glocal int ret = -1; glocal CONNECT_INFO *con = &con; glocal const char *session = session.ptr; glocal bool is_mini_photo = strstr(filename.ptr,"/public/mini-photo.jpg")!=nullptr; glocal bool is_photo = strstr(filename.ptr,"/public/photo.jpg")!=nullptr; (con,session,filename,"",false); if (success){ glocal.ret = 0; util_sendfile_common(*glocal.con,info,content,more,handle,glocal.session); }else if (glocal.is_mini_photo){ glocal.ret = util_sendfile("/var/www/html/no-mini-photo.jpg"); }else if (glocal.is_photo){ glocal.ret = util_sendfile("/var/www/html/no-photo.jpg"); }else{ } return glocal.ret; } /* Send a file using the public api. The file is /username/file. We accept also username/file. So we extract the username */ int util_sendpublicfile (CONNECT_INFO &con, PARAM_STRING filename) { glocal int ret = -1; glocal CONNECT_INFO *con = &con; const char *pt = filename.ptr; if (*pt == '/') pt++; const char *start = pt; pt = strchr(pt,'/'); if (pt != NULL){ glocal const char *filename = pt; glocal string username = string(start,pt-start); (con,glocal.username,pt,0); if (!success){ if (strcmp(glocal.filename,"/project/photo.jpg")==0){ glocal.ret = util_sendfile ("/var/www/html/no-photo.jpg"); }else if (strcmp(glocal.filename,"/project/mini-photo.jpg")==0){ glocal.ret = util_sendfile ("/var/www/html/no-mini-photo.jpg"); } if (glocal.ret != 0) tlmp_warning ("Can't read public file %s for user %s: %s\n",glocal.filename,glocal.username.c_str(),msg); }else{ glocal.ret = 0; util_sendfile_common(*glocal.con,info,content,more,handle,"public"); } } return glocal.ret; } string util_flipspaces(PARAM_STRING src) { string ret = src.ptr; for (auto &c:ret){ if (c == ' ') c = '_'; } return ret; } /* Extract a URL (if found) from a line. Return true if it was a URL. */ static bool util_is_url(const char *txt, const char *&end,string &url) { bool ret = false; const char *pt; if (is_start_any_ofnc(txt,pt,"http://","https://")){ pt = txt; while (*pt > ' ' && is_not_in(*pt,'"','>',',',')')) pt++; // A URL can't end with a period if (pt > txt && pt[-1] == '.') pt--; url = string(txt,pt-txt); end = pt; ret = true; } return ret; } /* Create a link to an internal part of the application using subformsubmit. This works inside content being itself clickable. */ string util_subformsubmit (PARAM_STRING link, PARAM_STRING text) { return string_f("%s\n" ,tlmpweb_curpage(),link.ptr,text.ptr); } /* Format and print a ... with openitab() */ void util_print_span(PARAM_STRING url) { htmlprintf ("%s\n",url.ptr,url.ptr); } /* Format a ... with openitab() */ static string util_span(PARAM_STRING url) { return string_f("%s",url.ptr,url.ptr); } /* Format an open ... with openitab() (no closing */ static string util_open_span(PARAM_STRING url) { return string(""; } void util_clickable_img (PARAM_STRING url, const char *image_width, unsigned border) { htmlout ("",image_width,border,url.ptr); htmlout (""); } /* Create the HTML for a clickable image (using javascript openintab()) */ string util_clickable_img (PARAM_STRING url, unsigned image_width) { string ret; ret.reserve(500); ret = util_open_span(url); ret += string_f(""; return ret; } /* Remove the dot at the end of a string */ static void util_remove_dot(string &line, string &dot) { if (line.size() > 0){ dot.clear(); auto last = line.size()-1; if (line[last] == '.'){ // The line ends with a period. It is probably not part of the ID. line.resize(last); dot = "."; } } } #define UTF8_ONE_BYTE_MASK 0b10000000 #define UTF8_ONE_BYTE_COUNT 0 #define UTF8_TWO_BYTE_MASK 0b11100000 #define UTF8_TWO_BYTE_COUNT 0b11000000 #define UTF8_THREE_BYTE_MASK 0b11110000 #define UTF8_THREE_BYTE_COUNT 0b11100000 #define UTF8_FOUR_BYTE_MASK 0b11111000 #define UTF8_FOUR_BYTE_COUNT 0b11110000 // This one could use a better name, I just don't know a better one (yet?) #define UTF8_OTHER_MASK 0b00111111 static size_t utf8_codepoint_size(uint8_t text) { if((text & UTF8_ONE_BYTE_MASK) == UTF8_ONE_BYTE_COUNT) { return 1; } if((text & UTF8_TWO_BYTE_MASK) == UTF8_TWO_BYTE_COUNT) { return 2; } if((text & UTF8_THREE_BYTE_MASK) == UTF8_THREE_BYTE_COUNT) { return 3; } return 4; } /* Copy a local link (user/project/document) Return the pointer */ static const char *util_copy_locallink(const char *txt, string &doc, string &dot) { const char *start = txt; while (*txt > ' ' && is_not_in(*txt,'"','\'','<','>',',','=','&')) txt++; doc = string(start,txt-start); util_remove_dot (doc,dot); return txt; } static const char *util_name_from_path(const string &doc) { const char *name = strrchr(doc.c_str(),'/'); if (name == nullptr){ name = doc.c_str(); }else{ name++; } return name; } // Present a video in a span static string util_span_video(const string &url, unsigned image_width) { string ret; ret += util_open_span(url); ret += string_f ("\n" ,image_width,url.c_str()); ret += ""; return ret; } // Validate that a document exist and has the proper type static void util_validate_doc ( CONNECT_INFO &con, const string &doc, string &errmsg, // Will contain the error message if any bool (*check)(FILE_TYPE file_type), const char *filenottype) // Error message { FILEINFO info; ENTRY_TYPE type = util_entrytype(con,string_f("/projects/%s",doc.c_str()),info); if (type == ENTRY_DIR){ errmsg = string_f(MSG_U(E_ISDIR,"Tag %s: %s is a folder\n"),"_IMG",doc.c_str()); }else if (is_not_in(type,ENTRY_FILE,ENTRY_DOCUMENT)){ errmsg = string_f(MSG_U(E_NOTFOUND,"Tag %s: document %s does not exist\n"),"_IMG",doc.c_str()); }else if (!check(info.file_type)){ errmsg = string_f(filenottype,doc.c_str()); } } /* Format a short message, remove the signature Escapes < and > Supports ? for URL if validate is true, does some validation. Errors are reported in errmsg. */ string util_format_shortmsg ( PARAM_STRING msg, unsigned nblines, size_t size, unsigned image_width, bool validate, CONNECT_INFO &con, string &errmsg) { const char *txt = msg.ptr; string ret; ret.reserve(nblines > 0 ? nblines*100 : 1000); unsigned pos=0; unsigned noline=0; if (nblines == 0) nblines=(unsigned)-1; while (*txt != '\0' && noline < nblines){ if (*txt == '\n'){ ret += "
\n"; pos=0; noline++; }else{ const char *pt; string url; if (util_is_url(txt,pt,url)){ ret += util_span (string(txt,pt-txt)); txt = pt-1; }else if (*txt == '<'){ if (is_start_any_ofnc(txt,pt,"
","","","","","","","","","","")){ while (txt < pt) ret += *txt++; txt--; // There is a txt++ at the end of the loop. }else{ ret += "<"; } }else if (*txt == '>'){ ret += ">"; }else if (*txt == '\t'){ unsigned nb = 4 - (pos%4); for (unsigned i=0; i\n"; ret += string_f ("\n" ,url.c_str()); ret += "
"; }else{ ret += "_IFRAME="; } txt--; }else if (is_start_any_of(txt,pt,"_HELP=")){ txt = pt; while (isalpha(*txt)) txt++; string tmp(pt,txt-pt); ret += string_f("%s\n" ,tlmpweb_curpage(),step_doctopic,tmp.c_str(),MSG_U(I_LINK,"link")); txt--; }else if (is_start_any_of(txt,pt,"_DOC=")){ txt = pt; string doc,dot; txt = util_copy_locallink(txt,doc,dot); if (doc.size() == 0){ ret += "_DOC="; if (validate){ errmsg = MSG_U(E_NODOCSPECIFIED,"Tag _DOC, no document specified"); } }else{ const char *name = util_name_from_path(doc); ret += string_f("%s\n" ,tlmpweb_curpage(),step_projects,doc.c_str(),name,doc.c_str()); ret += dot; if (validate){ FILEINFO info; ENTRY_TYPE type = util_entrytype(con,string_f("/projects/%s",doc.c_str()),info); if (type == ENTRY_DIR){ errmsg = string_f(MSG_R(E_ISDIR),"_DOC",doc.c_str()); }else if (is_not_in(type,ENTRY_FILE,ENTRY_DOCUMENT)){ errmsg = string_f(MSG_R(E_NOTFOUND),"_DOC",doc.c_str()); } } } txt--; }else if (is_start_any_of(txt,pt,"_USER=")){ txt = pt; while (*txt > ' ' && is_not_in(*txt,'"','>',',')) txt++; string user(pt,txt-pt); string dot; util_remove_dot (user,dot); if (user.size() == 0){ ret += "_USER="; }else{ ret += " "; if (user == "admin"){ // admin is not in the contact list of everyone, but has a public page ret += ""; }else{ string dir = string_f("/projects/%s/public",user.c_str()); unsigned mini_image_width = tlmpweb_ismobile() ? 60 : 40; ret += util_mini_img(step_image,mini_image_width,dir,"mini-photo.jpg",""); } string add = string_f("webstep=%d&webtab_add=1:%s:inbox~%s&recipients2=%s" ,step_talks,userinfo.name.c_str(),MSG_R(I_SHORTINBOX),user.c_str()); ret += " "; ret += util_subformsubmit(add,user); ret += dot; } txt--; }else{ size_t sizecar = utf8_codepoint_size(*txt); while (sizecar > 0){ ret += *txt++; sizecar--; } txt--; pos++; } } txt++; } if (*txt != '\0' || (size != 0 && size != strlen(msg.ptr))){ if (ret.size() > 0 && ret[ret.size()-1] != '\n') ret += "
\n"; ret += "..."; } return ret; } string util_format_shortmsg (PARAM_STRING msg, unsigned nblines, size_t size, unsigned image_width) { string errmsg; CONNECT_INFO con; return util_format_shortmsg (msg,nblines,size,image_width,false,con,errmsg); } string util_format_shortmsg (PARAM_STRING txt, unsigned image_width) { return util_format_shortmsg (txt,0,0,image_width); } // Send an HTML document. Does some remaping of relative URLs. Prevent scripting as well. void _F_public_page::sendhtml (const BOB_TYPE &content) { string tmp((const char*)content.getbuffer(),content.getsize()); const char *txt = tmp.c_str(); const char *tosend = txt; while (*txt != '\0'){ if (*txt == '<'){ // Check if this is