From 56176199f66dbd1ffda0ffafa562d55ff9b76624 Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Tue, 14 Nov 2017 11:59:11 +0100 Subject: [PATCH] Simplify Doxyfile. --- docs/{doxygen.config => Doxyfile} | 36 +++- docs/{user => }/html/annotated.html | 2 +- docs/{user => }/html/arrowdown.png | Bin docs/{user => }/html/arrowright.png | Bin docs/{user => }/html/bc_s.png | Bin docs/{user => }/html/bdwn.png | Bin docs/{user => }/html/classes.html | 2 +- docs/{user => }/html/closed.png | Bin .../dir_68267d1309a1af8e8297ef4c3efbcdba.html | 2 +- docs/{user => }/html/doc.png | Bin docs/{user => }/html/doxygen.css | 0 docs/{user => }/html/doxygen.png | Bin docs/{user => }/html/dynsections.js | 7 - docs/html/files.html | 126 ++++++++++++ docs/{user => }/html/folderclosed.png | Bin docs/{user => }/html/folderopen.png | Bin docs/{user => }/html/functions.html | 2 +- docs/{user => }/html/functions_func.html | 2 +- docs/{user => }/html/functions_rela.html | 2 +- docs/{user => }/html/functions_type.html | 2 +- docs/{user => }/html/functions_vars.html | 2 +- docs/{user => }/html/index.html | 2 +- docs/{user => }/html/jquery.js | 0 docs/{user => }/html/mraheader_8h_source.html | 2 +- .../html/mrapeerstate_8h_source.html | 2 +- docs/{user => }/html/mrchat_8h_source.html | 26 +-- .../{user => }/html/mrchatlist_8h_source.html | 24 +-- docs/{user => }/html/mrcontact_8h_source.html | 24 +-- docs/{user => }/html/mrdehtml_8h_source.html | 2 +- docs/{user => }/html/mrimap_8h_source.html | 4 +- docs/{user => }/html/mrjob_8h_source.html | 6 +- docs/{user => }/html/mrkey_8h_source.html | 4 +- docs/{user => }/html/mrkeyring_8h_source.html | 2 +- .../html/mrloginparam_8h_source.html | 2 +- docs/{user => }/html/mrmailbox_8h_source.html | 142 ++++++------- .../html/mrmailbox__internal_8h_source.html | 2 +- .../html/mrmimefactory_8h_source.html | 8 +- .../html/mrmimeparser_8h_source.html | 6 +- docs/{user => }/html/mrmsg_8h_source.html | 40 ++-- .../{user => }/html/mrosnative_8h_source.html | 4 +- docs/{user => }/html/mrparam_8h_source.html | 20 +- docs/{user => }/html/mrpgp_8h_source.html | 4 +- .../{user => }/html/mrpoortext_8h_source.html | 22 +- .../html/mrsaxparser_8h_source.html | 2 +- .../{user => }/html/mrsimplify_8h_source.html | 2 +- docs/{user => }/html/mrsmtp_8h_source.html | 4 +- docs/{user => }/html/mrsqlite3_8h_source.html | 4 +- docs/{user => }/html/mrstock_8h_source.html | 4 +- docs/{user => }/html/mrtools_8h_source.html | 4 +- docs/{user => }/html/nav_f.png | Bin docs/{user => }/html/nav_g.png | Bin docs/{user => }/html/nav_h.png | Bin docs/{user => }/html/open.png | Bin docs/{user => }/html/search/all_0.html | 0 docs/{user => }/html/search/all_0.js | 0 docs/{user => }/html/search/all_1.html | 0 docs/{user => }/html/search/all_1.js | 0 docs/{user => }/html/search/classes_0.html | 0 docs/{user => }/html/search/classes_0.js | 0 docs/{user => }/html/search/close.png | Bin docs/{user => }/html/search/functions_0.html | 0 docs/{user => }/html/search/functions_0.js | 0 docs/{user => }/html/search/mag_sel.png | Bin docs/{user => }/html/search/nomatches.html | 0 docs/{user => }/html/search/pages_0.html | 0 docs/{user => }/html/search/pages_0.js | 0 docs/{user => }/html/search/related_0.html | 0 docs/{user => }/html/search/related_0.js | 0 docs/{user => }/html/search/search.css | 0 docs/{user => }/html/search/search.js | 0 docs/{user => }/html/search/search_l.png | Bin docs/{user => }/html/search/search_m.png | Bin docs/{user => }/html/search/search_r.png | Bin docs/{user => }/html/search/searchdata.js | 0 docs/{user => }/html/search/typedefs_0.html | 0 docs/{user => }/html/search/typedefs_0.js | 0 docs/{user => }/html/search/variables_0.html | 0 docs/{user => }/html/search/variables_0.js | 0 docs/{user => }/html/splitbar.png | Bin .../html/structmrchat__t-members.html | 2 +- docs/{user => }/html/structmrchat__t.html | 14 +- .../html/structmrchatlist__t-members.html | 2 +- docs/{user => }/html/structmrchatlist__t.html | 18 +- .../html/structmrcontact__t-members.html | 2 +- docs/{user => }/html/structmrcontact__t.html | 14 +- .../html/structmrmailbox__t-members.html | 2 +- docs/{user => }/html/structmrmailbox__t.html | 138 +------------ .../html/structmrmsg__t-members.html | 2 +- docs/{user => }/html/structmrmsg__t.html | 42 +--- .../html/structmrparam__t-members.html | 2 +- docs/{user => }/html/structmrparam__t.html | 22 +- .../html/structmrpoortext__t-members.html | 2 +- docs/{user => }/html/structmrpoortext__t.html | 8 +- docs/{user => }/html/sync_off.png | Bin docs/{user => }/html/sync_on.png | Bin docs/{user => }/html/tab_a.png | Bin docs/{user => }/html/tab_b.png | Bin docs/{user => }/html/tab_h.png | Bin docs/{user => }/html/tab_s.png | Bin docs/{user => }/html/tabs.css | 0 docs/{user => }/html/user.css | 0 docs/user/html/files.html | 156 -------------- docs/user/html/mraheader_8c_source.html | 99 --------- docs/user/html/mrapeerstate_8c_source.html | 99 --------- docs/user/html/mrchat_8c_source.html | 112 ---------- docs/user/html/mrchatlist_8c_source.html | 128 ------------ docs/user/html/mrcontact_8c_source.html | 110 ---------- docs/user/html/mrdehtml_8c_source.html | 99 --------- docs/user/html/mrimap_8c_source.html | 100 --------- docs/user/html/mrjob_8c_source.html | 104 ---------- docs/user/html/mrkey_8c_source.html | 100 --------- docs/user/html/mrkeyring_8c_source.html | 99 --------- docs/user/html/mrloginparam_8c_source.html | 99 --------- docs/user/html/mrmailbox_8c_source.html | 192 ------------------ .../html/mrmailbox__configure_8c_source.html | 103 ---------- docs/user/html/mrmailbox__e2ee_8c_source.html | 100 --------- docs/user/html/mrmailbox__imex_8c_source.html | 108 ---------- docs/user/html/mrmailbox__log_8c_source.html | 100 --------- .../user/html/mrmailbox__tools_8c_source.html | 101 --------- docs/user/html/mrmimefactory_8c_source.html | 121 ----------- docs/user/html/mrmimeparser_8c_source.html | 104 ---------- docs/user/html/mrmsg_8c_source.html | 138 ------------- docs/user/html/mrosnative_8c_source.html | 100 --------- docs/user/html/mrparam_8c_source.html | 108 ---------- docs/user/html/mrpgp_8c_source.html | 100 --------- docs/user/html/mrpoortext_8c_source.html | 121 ----------- docs/user/html/mrsaxparser_8c_source.html | 99 --------- docs/user/html/mrsimplify_8c_source.html | 99 --------- docs/user/html/mrsmtp_8c_source.html | 100 --------- docs/user/html/mrsqlite3_8c_source.html | 100 --------- docs/user/html/mrstock_8c_source.html | 100 --------- docs/user/html/mrtools_8c_source.html | 100 --------- 132 files changed, 373 insertions(+), 3849 deletions(-) rename docs/{doxygen.config => Doxyfile} (98%) rename docs/{user => }/html/annotated.html (98%) rename docs/{user => }/html/arrowdown.png (100%) rename docs/{user => }/html/arrowright.png (100%) rename docs/{user => }/html/bc_s.png (100%) rename docs/{user => }/html/bdwn.png (100%) rename docs/{user => }/html/classes.html (98%) rename docs/{user => }/html/closed.png (100%) rename docs/{user => }/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html (98%) rename docs/{user => }/html/doc.png (100%) rename docs/{user => }/html/doxygen.css (100%) rename docs/{user => }/html/doxygen.png (100%) rename docs/{user => }/html/dynsections.js (91%) create mode 100644 docs/html/files.html rename docs/{user => }/html/folderclosed.png (100%) rename docs/{user => }/html/folderopen.png (100%) rename docs/{user => }/html/functions.html (99%) rename docs/{user => }/html/functions_func.html (99%) rename docs/{user => }/html/functions_rela.html (98%) rename docs/{user => }/html/functions_type.html (98%) rename docs/{user => }/html/functions_vars.html (99%) rename docs/{user => }/html/index.html (99%) rename docs/{user => }/html/jquery.js (100%) rename docs/{user => }/html/mraheader_8h_source.html (99%) rename docs/{user => }/html/mrapeerstate_8h_source.html (99%) rename docs/{user => }/html/mrchat_8h_source.html (94%) rename docs/{user => }/html/mrchatlist_8h_source.html (93%) rename docs/{user => }/html/mrcontact_8h_source.html (94%) rename docs/{user => }/html/mrdehtml_8h_source.html (99%) rename docs/{user => }/html/mrimap_8h_source.html (99%) rename docs/{user => }/html/mrjob_8h_source.html (97%) rename docs/{user => }/html/mrkey_8h_source.html (99%) rename docs/{user => }/html/mrkeyring_8h_source.html (99%) rename docs/{user => }/html/mrloginparam_8h_source.html (99%) rename docs/{user => }/html/mrmailbox_8h_source.html (93%) rename docs/{user => }/html/mrmailbox__internal_8h_source.html (99%) rename docs/{user => }/html/mrmimefactory_8h_source.html (97%) rename docs/{user => }/html/mrmimeparser_8h_source.html (98%) rename docs/{user => }/html/mrmsg_8h_source.html (94%) rename docs/{user => }/html/mrosnative_8h_source.html (98%) rename docs/{user => }/html/mrparam_8h_source.html (95%) rename docs/{user => }/html/mrpgp_8h_source.html (98%) rename docs/{user => }/html/mrpoortext_8h_source.html (93%) rename docs/{user => }/html/mrsaxparser_8h_source.html (99%) rename docs/{user => }/html/mrsimplify_8h_source.html (99%) rename docs/{user => }/html/mrsmtp_8h_source.html (98%) rename docs/{user => }/html/mrsqlite3_8h_source.html (99%) rename docs/{user => }/html/mrstock_8h_source.html (99%) rename docs/{user => }/html/mrtools_8h_source.html (99%) rename docs/{user => }/html/nav_f.png (100%) rename docs/{user => }/html/nav_g.png (100%) rename docs/{user => }/html/nav_h.png (100%) rename docs/{user => }/html/open.png (100%) rename docs/{user => }/html/search/all_0.html (100%) rename docs/{user => }/html/search/all_0.js (100%) rename docs/{user => }/html/search/all_1.html (100%) rename docs/{user => }/html/search/all_1.js (100%) rename docs/{user => }/html/search/classes_0.html (100%) rename docs/{user => }/html/search/classes_0.js (100%) rename docs/{user => }/html/search/close.png (100%) rename docs/{user => }/html/search/functions_0.html (100%) rename docs/{user => }/html/search/functions_0.js (100%) rename docs/{user => }/html/search/mag_sel.png (100%) rename docs/{user => }/html/search/nomatches.html (100%) rename docs/{user => }/html/search/pages_0.html (100%) rename docs/{user => }/html/search/pages_0.js (100%) rename docs/{user => }/html/search/related_0.html (100%) rename docs/{user => }/html/search/related_0.js (100%) rename docs/{user => }/html/search/search.css (100%) rename docs/{user => }/html/search/search.js (100%) rename docs/{user => }/html/search/search_l.png (100%) rename docs/{user => }/html/search/search_m.png (100%) rename docs/{user => }/html/search/search_r.png (100%) rename docs/{user => }/html/search/searchdata.js (100%) rename docs/{user => }/html/search/typedefs_0.html (100%) rename docs/{user => }/html/search/typedefs_0.js (100%) rename docs/{user => }/html/search/variables_0.html (100%) rename docs/{user => }/html/search/variables_0.js (100%) rename docs/{user => }/html/splitbar.png (100%) rename docs/{user => }/html/structmrchat__t-members.html (99%) rename docs/{user => }/html/structmrchat__t.html (94%) rename docs/{user => }/html/structmrchatlist__t-members.html (98%) rename docs/{user => }/html/structmrchatlist__t.html (93%) rename docs/{user => }/html/structmrcontact__t-members.html (99%) rename docs/{user => }/html/structmrcontact__t.html (93%) rename docs/{user => }/html/structmrmailbox__t-members.html (99%) rename docs/{user => }/html/structmrmailbox__t.html (93%) rename docs/{user => }/html/structmrmsg__t-members.html (99%) rename docs/{user => }/html/structmrmsg__t.html (91%) rename docs/{user => }/html/structmrparam__t-members.html (98%) rename docs/{user => }/html/structmrparam__t.html (92%) rename docs/{user => }/html/structmrpoortext__t-members.html (99%) rename docs/{user => }/html/structmrpoortext__t.html (95%) rename docs/{user => }/html/sync_off.png (100%) rename docs/{user => }/html/sync_on.png (100%) rename docs/{user => }/html/tab_a.png (100%) rename docs/{user => }/html/tab_b.png (100%) rename docs/{user => }/html/tab_h.png (100%) rename docs/{user => }/html/tab_s.png (100%) rename docs/{user => }/html/tabs.css (100%) rename docs/{user => }/html/user.css (100%) delete mode 100644 docs/user/html/files.html delete mode 100644 docs/user/html/mraheader_8c_source.html delete mode 100644 docs/user/html/mrapeerstate_8c_source.html delete mode 100644 docs/user/html/mrchat_8c_source.html delete mode 100644 docs/user/html/mrchatlist_8c_source.html delete mode 100644 docs/user/html/mrcontact_8c_source.html delete mode 100644 docs/user/html/mrdehtml_8c_source.html delete mode 100644 docs/user/html/mrimap_8c_source.html delete mode 100644 docs/user/html/mrjob_8c_source.html delete mode 100644 docs/user/html/mrkey_8c_source.html delete mode 100644 docs/user/html/mrkeyring_8c_source.html delete mode 100644 docs/user/html/mrloginparam_8c_source.html delete mode 100644 docs/user/html/mrmailbox_8c_source.html delete mode 100644 docs/user/html/mrmailbox__configure_8c_source.html delete mode 100644 docs/user/html/mrmailbox__e2ee_8c_source.html delete mode 100644 docs/user/html/mrmailbox__imex_8c_source.html delete mode 100644 docs/user/html/mrmailbox__log_8c_source.html delete mode 100644 docs/user/html/mrmailbox__tools_8c_source.html delete mode 100644 docs/user/html/mrmimefactory_8c_source.html delete mode 100644 docs/user/html/mrmimeparser_8c_source.html delete mode 100644 docs/user/html/mrmsg_8c_source.html delete mode 100644 docs/user/html/mrosnative_8c_source.html delete mode 100644 docs/user/html/mrparam_8c_source.html delete mode 100644 docs/user/html/mrpgp_8c_source.html delete mode 100644 docs/user/html/mrpoortext_8c_source.html delete mode 100644 docs/user/html/mrsaxparser_8c_source.html delete mode 100644 docs/user/html/mrsimplify_8c_source.html delete mode 100644 docs/user/html/mrsmtp_8c_source.html delete mode 100644 docs/user/html/mrsqlite3_8c_source.html delete mode 100644 docs/user/html/mrstock_8c_source.html delete mode 100644 docs/user/html/mrtools_8c_source.html diff --git a/docs/doxygen.config b/docs/Doxyfile similarity index 98% rename from docs/doxygen.config rename to docs/Doxyfile index 696c7bfc..efbff8d1 100644 --- a/docs/doxygen.config +++ b/docs/Doxyfile @@ -32,7 +32,9 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. +###################################################### PROJECT_NAME = "Delta Chat Core C-Library" +###################################################### # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -58,7 +60,9 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = user +###################################################### +OUTPUT_DIRECTORY = . +###################################################### # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -177,7 +181,9 @@ SHORT_NAMES = NO # description.) # The default value is: NO. +###################################################### JAVADOC_AUTOBRIEF = YES +###################################################### # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If @@ -185,7 +191,7 @@ JAVADOC_AUTOBRIEF = YES # requiring an explicit \brief command for a brief description.) # The default value is: NO. -QT_AUTOBRIEF = YES +QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as @@ -241,9 +247,11 @@ TCL_SUBST = # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. -# !! we do not set this for Delta Chat as we use C in an object-orientated way, so "Class" is better than "Data structure" for us. !! +###################################################### +# we do not set this for Delta Chat as we use C in an object-orientated way, so "Class" is better than "Data structure" for us. !! OPTIMIZE_OUTPUT_FOR_C = NO +###################################################### # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -443,7 +451,9 @@ EXTRACT_STATIC = NO # for Java sources. # The default value is: YES. +###################################################### EXTRACT_LOCAL_CLASSES = NO +###################################################### # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are @@ -772,7 +782,9 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. +###################################################### INPUT = ../src/ +###################################################### # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -839,8 +851,10 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* +###################################################### EXCLUDE_SYMBOLS = mraheader_t mrapeerstate_t mrmailbox_e2ee_helper_t mrimap_t mrjob_t mrkey_t mrkeyring_t mrloginparam_t mrmime*_t EXCLUDE_SYMBOLS += mrsaxparser_t mrsimplify_t mrsmtp_t mrsqlite3_t mrstrbuilder_t +###################################################### # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -935,7 +949,11 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = YES +###################################################### +# we do use a browser as there is a high risk the user sees the wrong code! +# however, this may change in the future +SOURCE_BROWSER = NO +###################################################### # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -1038,7 +1056,9 @@ CLANG_OPTIONS = # classes, structs, unions or interfaces. # The default value is: YES. +###################################################### ALPHABETICAL_INDEX = NO +###################################################### # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. @@ -1132,7 +1152,9 @@ HTML_STYLESHEET = # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. +###################################################### HTML_EXTRA_STYLESHEET = user.css +###################################################### # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1181,7 +1203,9 @@ HTML_COLORSTYLE_GAMMA = 80 # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. +###################################################### HTML_TIMESTAMP = YES +###################################################### # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the @@ -1606,7 +1630,9 @@ EXTRA_SEARCH_MAPPINGS = # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. +###################################################### GENERATE_LATEX = NO +###################################################### # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -2154,7 +2180,9 @@ HIDE_UNDOC_RELATIONS = YES # set to NO # The default value is: YES. +###################################################### HAVE_DOT = NO +###################################################### # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of diff --git a/docs/user/html/annotated.html b/docs/html/annotated.html similarity index 98% rename from docs/user/html/annotated.html rename to docs/html/annotated.html index fd48ec50..fee9f8a3 100644 --- a/docs/user/html/annotated.html +++ b/docs/html/annotated.html @@ -99,7 +99,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/arrowdown.png b/docs/html/arrowdown.png similarity index 100% rename from docs/user/html/arrowdown.png rename to docs/html/arrowdown.png diff --git a/docs/user/html/arrowright.png b/docs/html/arrowright.png similarity index 100% rename from docs/user/html/arrowright.png rename to docs/html/arrowright.png diff --git a/docs/user/html/bc_s.png b/docs/html/bc_s.png similarity index 100% rename from docs/user/html/bc_s.png rename to docs/html/bc_s.png diff --git a/docs/user/html/bdwn.png b/docs/html/bdwn.png similarity index 100% rename from docs/user/html/bdwn.png rename to docs/html/bdwn.png diff --git a/docs/user/html/classes.html b/docs/html/classes.html similarity index 98% rename from docs/user/html/classes.html rename to docs/html/classes.html index 421912cf..13093407 100644 --- a/docs/user/html/classes.html +++ b/docs/html/classes.html @@ -97,7 +97,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/closed.png b/docs/html/closed.png similarity index 100% rename from docs/user/html/closed.png rename to docs/html/closed.png diff --git a/docs/user/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html b/docs/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html similarity index 98% rename from docs/user/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html rename to docs/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html index 81adb637..68726907 100644 --- a/docs/user/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html +++ b/docs/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html @@ -95,7 +95,7 @@ Files diff --git a/docs/user/html/doc.png b/docs/html/doc.png similarity index 100% rename from docs/user/html/doc.png rename to docs/html/doc.png diff --git a/docs/user/html/doxygen.css b/docs/html/doxygen.css similarity index 100% rename from docs/user/html/doxygen.css rename to docs/html/doxygen.css diff --git a/docs/user/html/doxygen.png b/docs/html/doxygen.png similarity index 100% rename from docs/user/html/doxygen.png rename to docs/html/doxygen.png diff --git a/docs/user/html/dynsections.js b/docs/html/dynsections.js similarity index 91% rename from docs/user/html/dynsections.js rename to docs/html/dynsections.js index 1e6bf07f..85e18369 100644 --- a/docs/user/html/dynsections.js +++ b/docs/html/dynsections.js @@ -95,10 +95,3 @@ function toggleInherit(id) } } - -$(document).ready(function() { - $('.code,.codeRef').each(function() { - $(this).data('powertip',$('#'+$(this).attr('href').replace(/.*\//,'').replace(/[^a-z_A-Z0-9]/g,'_')).html()); - $(this).powerTip({ placement: 's', smartPlacement: true, mouseOnToPopup: true }); - }); -}); diff --git a/docs/html/files.html b/docs/html/files.html new file mode 100644 index 00000000..29c7091b --- /dev/null +++ b/docs/html/files.html @@ -0,0 +1,126 @@ + + + + + + +Delta Chat Core C-Library: File List + + + + + + + + + + + +
+
+ + + + + + +
+
Delta Chat Core C-Library +
+
+
+ + + + + +
+ +
+
+ + +
+ +
+ +
+
+
File List
+
+
+
Here is a list of all documented files with brief descriptions:
+
[detail level 12]
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
  src
 mraheader.h
 mrapeerstate.h
 mrchat.h
 mrchatlist.h
 mrcontact.h
 mrdehtml.h
 mrimap.h
 mrjob.h
 mrkey.h
 mrkeyring.h
 mrloginparam.h
 mrmailbox.h
 mrmailbox_internal.h
 mrmimefactory.h
 mrmimeparser.h
 mrmsg.h
 mrosnative.h
 mrparam.h
 mrpgp.h
 mrpoortext.h
 mrsaxparser.h
 mrsimplify.h
 mrsmtp.h
 mrsqlite3.h
 mrstock.h
 mrtools.h
+
+
+ + + + diff --git a/docs/user/html/folderclosed.png b/docs/html/folderclosed.png similarity index 100% rename from docs/user/html/folderclosed.png rename to docs/html/folderclosed.png diff --git a/docs/user/html/folderopen.png b/docs/html/folderopen.png similarity index 100% rename from docs/user/html/folderopen.png rename to docs/html/folderopen.png diff --git a/docs/user/html/functions.html b/docs/html/functions.html similarity index 99% rename from docs/user/html/functions.html rename to docs/html/functions.html index 8c145d5f..62ffd4c4 100644 --- a/docs/user/html/functions.html +++ b/docs/html/functions.html @@ -477,7 +477,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/functions_func.html b/docs/html/functions_func.html similarity index 99% rename from docs/user/html/functions_func.html rename to docs/html/functions_func.html index 5b0a2f8d..065535f3 100644 --- a/docs/user/html/functions_func.html +++ b/docs/html/functions_func.html @@ -381,7 +381,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/functions_rela.html b/docs/html/functions_rela.html similarity index 98% rename from docs/user/html/functions_rela.html rename to docs/html/functions_rela.html index 9abe3450..7af0cb0e 100644 --- a/docs/user/html/functions_rela.html +++ b/docs/html/functions_rela.html @@ -107,7 +107,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/functions_type.html b/docs/html/functions_type.html similarity index 98% rename from docs/user/html/functions_type.html rename to docs/html/functions_type.html index abff79e9..b88d7a53 100644 --- a/docs/user/html/functions_type.html +++ b/docs/html/functions_type.html @@ -98,7 +98,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/functions_vars.html b/docs/html/functions_vars.html similarity index 99% rename from docs/user/html/functions_vars.html rename to docs/html/functions_vars.html index aebebfa7..5a99546a 100644 --- a/docs/user/html/functions_vars.html +++ b/docs/html/functions_vars.html @@ -183,7 +183,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/index.html b/docs/html/index.html similarity index 99% rename from docs/user/html/index.html rename to docs/html/index.html index c77ef64b..c86cfd3e 100644 --- a/docs/user/html/index.html +++ b/docs/html/index.html @@ -90,7 +90,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/jquery.js b/docs/html/jquery.js similarity index 100% rename from docs/user/html/jquery.js rename to docs/html/jquery.js diff --git a/docs/user/html/mraheader_8h_source.html b/docs/html/mraheader_8h_source.html similarity index 99% rename from docs/user/html/mraheader_8h_source.html rename to docs/html/mraheader_8h_source.html index a6da68d9..02fec62f 100644 --- a/docs/user/html/mraheader_8h_source.html +++ b/docs/html/mraheader_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRAHEADER_H__
24 #define __MRAHEADER_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #include "mrkey.h"
31 
32 
36 typedef struct mraheader_t
37 {
38  char* m_addr;
39  mrkey_t* m_public_key; /* != NULL */
40  int m_prefer_encrypt; /* YES, NO or NOPREFERENCE if attribute is missing */
41 } mraheader_t;
42 
43 
44 mraheader_t* mraheader_new (); /* the returned pointer is ref'd and must be unref'd after usage */
45 mraheader_t* mraheader_new_from_imffields(const char* wanted_from, const struct mailimf_fields* mime);
46 void mraheader_empty (mraheader_t*);
47 void mraheader_unref (mraheader_t*);
48 
49 int mraheader_set_from_string (mraheader_t*, const char* header_str);
50 
51 char* mraheader_render (const mraheader_t*);
52 
53 
54 #ifdef __cplusplus
55 } /* /extern "C" */
56 #endif
57 #endif /* __MRAHEADER_H__ */
diff --git a/docs/user/html/mrapeerstate_8h_source.html b/docs/html/mrapeerstate_8h_source.html similarity index 99% rename from docs/user/html/mrapeerstate_8h_source.html rename to docs/html/mrapeerstate_8h_source.html index a11732a2..c37a5523 100644 --- a/docs/user/html/mrapeerstate_8h_source.html +++ b/docs/html/mrapeerstate_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRAPEERSTATE_H__
24 #define __MRAPEERSTATE_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #include "mrkey.h"
31 
32 
33 typedef struct mraheader_t mraheader_t;
34 
35 
36 #define MRA_PE_NOPREFERENCE 0 /* prefer-encrypt states */
37 #define MRA_PE_MUTUAL 1
38 #define MRA_PE_GOSSIP 2
39 #define MRA_PE_RESET 20
40 
41 
45 typedef struct mrapeerstate_t
46 {
48  char* m_addr;
49  time_t m_last_seen;
50  time_t m_last_seen_autocrypt;
51  mrkey_t* m_public_key;
52  int m_prefer_encrypt;
53 
54  #define MRA_SAVE_LAST_SEEN 0x01
55  #define MRA_SAVE_ALL 0x02
56  int m_to_save;
57 } mrapeerstate_t;
58 
59 
60 mrapeerstate_t* mrapeerstate_new (); /* the returned pointer is ref'd and must be unref'd after usage */
61 void mrapeerstate_unref (mrapeerstate_t*);
62 
63 int mrapeerstate_init_from_header (mrapeerstate_t*, const mraheader_t*, time_t message_time);
64 int mrapeerstate_degrade_encryption(mrapeerstate_t*, time_t message_time);
65 int mrapeerstate_apply_header (mrapeerstate_t*, const mraheader_t*, time_t message_time); /*returns 1 on changes*/
66 
67 int mrapeerstate_load_from_db__ (mrapeerstate_t*, mrsqlite3_t*, const char* addr);
68 int mrapeerstate_save_to_db__ (const mrapeerstate_t*, mrsqlite3_t*, int create);
69 
70 
71 #ifdef __cplusplus
72 } /* /extern "C" */
73 #endif
74 #endif /* __MRAPEERSTATE_H__ */
75 
diff --git a/docs/user/html/mrchat_8h_source.html b/docs/html/mrchat_8h_source.html similarity index 94% rename from docs/user/html/mrchat_8h_source.html rename to docs/html/mrchat_8h_source.html index e61fc237..641c55f3 100644 --- a/docs/user/html/mrchat_8h_source.html +++ b/docs/html/mrchat_8h_source.html @@ -88,22 +88,22 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrchat.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRCHAT_H__
24 #define __MRCHAT_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct mrparam_t mrparam_t;
32 
33 
39 typedef struct mrchat_t
40 {
41  #define MR_CHAT_ID_DEADDROP 1 /* messages send from unknown/unwanted users to us, chats_contacts is not set up. This group may be shown normally. */
42  #define MR_CHAT_ID_TO_DEADDROP 2 /* messages send from us to unknown/unwanted users (this may happen when deleting chats or when using CC: in the email-program) */
43  #define MR_CHAT_ID_TRASH 3 /* messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again) */
44  #define MR_CHAT_ID_MSGS_IN_CREATION 4 /* a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be send and shown) */
45  #define MR_CHAT_ID_STARRED 5 /* virtual chat containing all starred messages */
46  #define MR_CHAT_ID_ARCHIVED_LINK 6 /* a link at the end of the chatlist, if present the UI should show the button "Archived chats" */
47  #define MR_CHAT_ID_LAST_SPECIAL 9 /* larger chat IDs are "real" chats, their messages are "real" messages. */
48  uint32_t m_id;
49 
50  #define MR_CHAT_TYPE_UNDEFINED 0
51  #define MR_CHAT_TYPE_NORMAL 100 /* a normal chat is a chat with a single contact, chats_contacts contains one record for the user, MR_CONTACT_ID_SELF is not added. */
52  #define MR_CHAT_TYPE_GROUP 120 /* a group chat, chats_contacts conain all group members, incl. MR_CONTACT_ID_SELF */
53  int m_type;
54 
55  char* m_name;
57  char* m_draft_text;
59  int m_archived;
63  char* m_grpid; /* NULL if unset */
64 } mrchat_t;
65 
66 
67 mrchat_t* mrchat_new (mrmailbox_t*);
68 void mrchat_empty (mrchat_t*);
69 void mrchat_unref (mrchat_t*);
71 
72 /* library-internal */
73 int mrchat_load_from_db__ (mrchat_t*, uint32_t id);
74 int mrchat_update_param__ (mrchat_t*);
75 
76 #define MR_CHAT_PREFIX "Chat:" /* you MUST NOT modify this or the following strings */
77 #define MR_CHATS_FOLDER "Chats" /* if we want to support Gma'l-labels - "Chats" is a reserved word for Gma'l */
78 
79 
80 #ifdef __cplusplus
81 } /* /extern "C" */
82 #endif
83 #endif /* __MRCHAT_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
-
char * m_draft_text
NULL if unset.
Definition: mrchat.h:57
-
mrmailbox_t * m_mailbox
!= NULL
Definition: mrchat.h:58
-
mrparam_t * m_param
!= NULL
Definition: mrchat.h:60
-
int m_archived
1=chat archived, this state should always be shown the UI, eg.
Definition: mrchat.h:59
-
char * m_name
NULL if unset.
Definition: mrchat.h:55
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
-
void mrchat_empty(mrchat_t *ths)
Empty a chat object.
Definition: mrchat.c:181
-
time_t m_draft_timestamp
0 if there is no draft
Definition: mrchat.h:56
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
char * mrchat_get_subtitle(mrchat_t *chat)
Get a subtitle for a chat.
Definition: mrchat.c:215
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRCHAT_H__
24 #define __MRCHAT_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct mrparam_t mrparam_t;
32 
33 
39 typedef struct mrchat_t
40 {
41  #define MR_CHAT_ID_DEADDROP 1 /* messages send from unknown/unwanted users to us, chats_contacts is not set up. This group may be shown normally. */
42  #define MR_CHAT_ID_TO_DEADDROP 2 /* messages send from us to unknown/unwanted users (this may happen when deleting chats or when using CC: in the email-program) */
43  #define MR_CHAT_ID_TRASH 3 /* messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again) */
44  #define MR_CHAT_ID_MSGS_IN_CREATION 4 /* a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be send and shown) */
45  #define MR_CHAT_ID_STARRED 5 /* virtual chat containing all starred messages */
46  #define MR_CHAT_ID_ARCHIVED_LINK 6 /* a link at the end of the chatlist, if present the UI should show the button "Archived chats" */
47  #define MR_CHAT_ID_LAST_SPECIAL 9 /* larger chat IDs are "real" chats, their messages are "real" messages. */
48  uint32_t m_id;
49 
50  #define MR_CHAT_TYPE_UNDEFINED 0
51  #define MR_CHAT_TYPE_NORMAL 100 /* a normal chat is a chat with a single contact, chats_contacts contains one record for the user, MR_CONTACT_ID_SELF is not added. */
52  #define MR_CHAT_TYPE_GROUP 120 /* a group chat, chats_contacts conain all group members, incl. MR_CONTACT_ID_SELF */
53  int m_type;
54 
55  char* m_name;
57  char* m_draft_text;
59  int m_archived;
63  char* m_grpid; /* NULL if unset */
64 } mrchat_t;
65 
66 
67 mrchat_t* mrchat_new (mrmailbox_t*);
68 void mrchat_empty (mrchat_t*);
69 void mrchat_unref (mrchat_t*);
71 
72 /* library-internal */
73 int mrchat_load_from_db__ (mrchat_t*, uint32_t id);
74 int mrchat_update_param__ (mrchat_t*);
75 
76 #define MR_CHAT_PREFIX "Chat:" /* you MUST NOT modify this or the following strings */
77 #define MR_CHATS_FOLDER "Chats" /* if we want to support Gma'l-labels - "Chats" is a reserved word for Gma'l */
78 
79 
80 #ifdef __cplusplus
81 } /* /extern "C" */
82 #endif
83 #endif /* __MRCHAT_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
+
char * m_draft_text
NULL if unset.
Definition: mrchat.h:57
+
mrmailbox_t * m_mailbox
!= NULL
Definition: mrchat.h:58
+
mrparam_t * m_param
!= NULL
Definition: mrchat.h:60
+
int m_archived
1=chat archived, this state should always be shown the UI, eg.
Definition: mrchat.h:59
+
char * m_name
NULL if unset.
Definition: mrchat.h:55
+
An object for handling key=value parameter lists.
Definition: mrparam.h:36
+
void mrchat_empty(mrchat_t *ths)
Empty a chat object.
Definition: mrchat.c:181
+
time_t m_draft_timestamp
0 if there is no draft
Definition: mrchat.h:56
+
An object representing a single chat in memory.
Definition: mrchat.h:39
+
char * mrchat_get_subtitle(mrchat_t *chat)
Get a subtitle for a chat.
Definition: mrchat.c:215
diff --git a/docs/user/html/mrchatlist_8h_source.html b/docs/html/mrchatlist_8h_source.html similarity index 93% rename from docs/user/html/mrchatlist_8h_source.html rename to docs/html/mrchatlist_8h_source.html index 86815e89..ee3c95f6 100644 --- a/docs/user/html/mrchatlist_8h_source.html +++ b/docs/html/mrchatlist_8h_source.html @@ -88,21 +88,21 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrchatlist.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRCHATLIST_H__
24 #define __MRCHATLIST_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct mrpoortext_t mrpoortext_t;
32 typedef struct mrchat_t mrchat_t;
33 
34 
42 typedef struct mrchatlist_t
43 {
47  #define MR_CHATLIST_IDS_PER_RESULT 2
48  size_t m_cnt;
49  carray* m_chatNlastmsg_ids;
50 } mrchatlist_t;
51 
52 
53 mrchatlist_t* mrchatlist_new (mrmailbox_t*);
57 uint32_t mrchatlist_get_chat_id (mrchatlist_t*, size_t index);
58 uint32_t mrchatlist_get_msg_id (mrchatlist_t*, size_t index);
60 
61 /* library-internal */
62 int mrchatlist_load_from_db__ (mrchatlist_t*, int listflags, const char* query);
63 
64 
65 #ifdef __cplusplus
66 } /* /extern "C" */
67 #endif
68 #endif /* __MRCHATLIST_H__ */
mrmailbox_t * m_mailbox
The mailbox, the chatlist belongs to.
Definition: mrchatlist.h:44
-
uint32_t mrchatlist_get_msg_id(mrchatlist_t *ths, size_t index)
Get a single message ID of a chatlist.
Definition: mrchatlist.c:151
-
An object representing a single chatlist in memory.
Definition: mrchatlist.h:42
-
mrpoortext_t * mrchatlist_get_summary(mrchatlist_t *chatlist, size_t index, mrchat_t *chat)
Get a summary for a chatlist index.
Definition: mrchatlist.c:201
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrchatlist_empty(mrchatlist_t *chatlist)
Empty a chatlist object.
Definition: mrchatlist.c:83
-
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
-
size_t mrchatlist_get_cnt(mrchatlist_t *chatlist)
Find out the number of chats in a chatlist.
Definition: mrchatlist.c:101
-
uint32_t mrchatlist_get_chat_id(mrchatlist_t *ths, size_t index)
Get a single chat ID of a chatlist.
Definition: mrchatlist.c:121
-
void mrchatlist_unref(mrchatlist_t *chatlist)
Free a mrchatlist_t object as created eg.
Definition: mrchatlist.c:62
-
An object representing a single chat in memory.
Definition: mrchat.h:39
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRCHATLIST_H__
24 #define __MRCHATLIST_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct mrpoortext_t mrpoortext_t;
32 typedef struct mrchat_t mrchat_t;
33 
34 
42 typedef struct mrchatlist_t
43 {
47  #define MR_CHATLIST_IDS_PER_RESULT 2
48  size_t m_cnt;
49  carray* m_chatNlastmsg_ids;
50 } mrchatlist_t;
51 
52 
53 mrchatlist_t* mrchatlist_new (mrmailbox_t*);
57 uint32_t mrchatlist_get_chat_id (mrchatlist_t*, size_t index);
58 uint32_t mrchatlist_get_msg_id (mrchatlist_t*, size_t index);
60 
61 /* library-internal */
62 int mrchatlist_load_from_db__ (mrchatlist_t*, int listflags, const char* query);
63 
64 
65 #ifdef __cplusplus
66 } /* /extern "C" */
67 #endif
68 #endif /* __MRCHATLIST_H__ */
mrmailbox_t * m_mailbox
The mailbox, the chatlist belongs to.
Definition: mrchatlist.h:44
+
uint32_t mrchatlist_get_msg_id(mrchatlist_t *ths, size_t index)
Get a single message ID of a chatlist.
Definition: mrchatlist.c:151
+
An object representing a single chatlist in memory.
Definition: mrchatlist.h:42
+
mrpoortext_t * mrchatlist_get_summary(mrchatlist_t *chatlist, size_t index, mrchat_t *chat)
Get a summary for a chatlist index.
Definition: mrchatlist.c:201
+
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
void mrchatlist_empty(mrchatlist_t *chatlist)
Empty a chatlist object.
Definition: mrchatlist.c:83
+
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
+
size_t mrchatlist_get_cnt(mrchatlist_t *chatlist)
Find out the number of chats in a chatlist.
Definition: mrchatlist.c:101
+
uint32_t mrchatlist_get_chat_id(mrchatlist_t *ths, size_t index)
Get a single chat ID of a chatlist.
Definition: mrchatlist.c:121
+
void mrchatlist_unref(mrchatlist_t *chatlist)
Free a mrchatlist_t object as created eg.
Definition: mrchatlist.c:62
+
An object representing a single chat in memory.
Definition: mrchat.h:39
diff --git a/docs/user/html/mrcontact_8h_source.html b/docs/html/mrcontact_8h_source.html similarity index 94% rename from docs/user/html/mrcontact_8h_source.html rename to docs/html/mrcontact_8h_source.html index 5d2df3a3..c90380f3 100644 --- a/docs/user/html/mrcontact_8h_source.html +++ b/docs/html/mrcontact_8h_source.html @@ -88,21 +88,21 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrcontact.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRCONTACT_H__
24 #define __MRCONTACT_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrsqlite3_t mrsqlite3_t;
31 
32 
38 typedef struct mrcontact_t
39 {
40  #define MR_CONTACT_ID_SELF 1
41  #define MR_CONTACT_ID_SYSTEM 2
42  #define MR_CONTACT_ID_LAST_SPECIAL 9
43  uint32_t m_id;
45  char* m_name;
46  char* m_authname;
47  char* m_addr;
48  int m_blocked;
51  int m_origin;
52 } mrcontact_t;
53 
54 
55 mrcontact_t* mrcontact_new (); /* the returned pointer is ref'd and must be unref'd after usage */
58 
59 
60 /* contact origins */
61 #define MR_ORIGIN_UNSET 0
62 #define MR_ORIGIN_INCOMING_UNKNOWN_FROM 0x10 /* From: of incoming messages of unknown sender */
63 #define MR_ORIGIN_INCOMING_UNKNOWN_CC 0x20 /* Cc: of incoming messages of unknown sender */
64 #define MR_ORIGIN_INCOMING_UNKNOWN_TO 0x40 /* To: of incoming messages of unknown sender */
65 #define MR_ORIGIN_INCOMING_REPLY_TO 0x100 /* Reply-To: of incoming message of known sender */
66 #define MR_ORIGIN_INCOMING_CC 0x200 /* Cc: of incoming message of known sender */
67 #define MR_ORIGIN_INCOMING_TO 0x400 /* additional To:'s of incoming message of known sender */
68 #define MR_ORIGIN_CREATE_CHAT 0x800 /* a chat was manually created for this user, but no message yet sent */
69 #define MR_ORIGIN_OUTGOING_BCC 0x1000 /* message send by us */
70 #define MR_ORIGIN_OUTGOING_CC 0x2000 /* message send by us */
71 #define MR_ORIGIN_OUTGOING_TO 0x4000 /* message send by us */
72 #define MR_ORIGIN_INTERNAL 0x40000 /* internal use */
73 #define MR_ORIGIN_ADRESS_BOOK 0x80000 /* address is in our address book */
74 #define MR_ORIGIN_MANUALLY_CREATED 0x100000 /* contact added by mrmailbox_create_contact() */
75 
76 #define MR_ORIGIN_MIN_CONTACT_LIST (MR_ORIGIN_INCOMING_REPLY_TO) /* contacts with at least this origin value are shown in the contact list */
77 #define MR_ORIGIN_MIN_VERIFIED (MR_ORIGIN_INCOMING_REPLY_TO) /* contacts with at least this origin value are verified and known not to be spam */
78 #define MR_ORIGIN_MIN_START_NEW_NCHAT (0x7FFFFFFF) /* contacts with at least this origin value start a new "normal" chat, defaults to off */
79 
80 
81 /* library-internal */
82 char* mrcontact_get_first_name (const char* full_name);
83 void mrcontact_normalize_name (char* full_name);
84 int mrcontact_load_from_db__ (mrcontact_t*, mrsqlite3_t*, uint32_t contact_id);
85 
86 
87 #ifdef __cplusplus
88 } /* /extern "C" */
89 #endif
90 #endif /* __MRCONTACT_H__ */
char * mrcontact_get_first_name(const char *full_name)
Get the first name.
Definition: mrcontact.c:99
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
void mrcontact_empty(mrcontact_t *ths)
Empty a contact object.
Definition: mrcontact.c:65
-
mrcontact_t * mrcontact_new()
Create a new contact object in memory.
Definition: mrcontact.c:32
-
void mrcontact_unref(mrcontact_t *ths)
Free a contact object.
Definition: mrcontact.c:49
-
void mrcontact_normalize_name(char *full_name)
Normalize a name in-place.
Definition: mrcontact.c:130
-
char * m_authname
may be NULL or empty, this is the name authorized by the sender, only this name may be speaded to oth...
Definition: mrcontact.h:46
-
int m_blocked
Blocked state.
Definition: mrcontact.h:48
-
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
-
char * m_name
may be NULL or empty, this name should not be spreaded as it may be "Daddy" and so on; initially set ...
Definition: mrcontact.h:45
-
uint32_t m_id
The contact ID.
Definition: mrcontact.h:43
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRCONTACT_H__
24 #define __MRCONTACT_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrsqlite3_t mrsqlite3_t;
31 
32 
38 typedef struct mrcontact_t
39 {
40  #define MR_CONTACT_ID_SELF 1
41  #define MR_CONTACT_ID_SYSTEM 2
42  #define MR_CONTACT_ID_LAST_SPECIAL 9
43  uint32_t m_id;
45  char* m_name;
46  char* m_authname;
47  char* m_addr;
48  int m_blocked;
51  int m_origin;
52 } mrcontact_t;
53 
54 
55 mrcontact_t* mrcontact_new (); /* the returned pointer is ref'd and must be unref'd after usage */
58 
59 
60 /* contact origins */
61 #define MR_ORIGIN_UNSET 0
62 #define MR_ORIGIN_INCOMING_UNKNOWN_FROM 0x10 /* From: of incoming messages of unknown sender */
63 #define MR_ORIGIN_INCOMING_UNKNOWN_CC 0x20 /* Cc: of incoming messages of unknown sender */
64 #define MR_ORIGIN_INCOMING_UNKNOWN_TO 0x40 /* To: of incoming messages of unknown sender */
65 #define MR_ORIGIN_INCOMING_REPLY_TO 0x100 /* Reply-To: of incoming message of known sender */
66 #define MR_ORIGIN_INCOMING_CC 0x200 /* Cc: of incoming message of known sender */
67 #define MR_ORIGIN_INCOMING_TO 0x400 /* additional To:'s of incoming message of known sender */
68 #define MR_ORIGIN_CREATE_CHAT 0x800 /* a chat was manually created for this user, but no message yet sent */
69 #define MR_ORIGIN_OUTGOING_BCC 0x1000 /* message send by us */
70 #define MR_ORIGIN_OUTGOING_CC 0x2000 /* message send by us */
71 #define MR_ORIGIN_OUTGOING_TO 0x4000 /* message send by us */
72 #define MR_ORIGIN_INTERNAL 0x40000 /* internal use */
73 #define MR_ORIGIN_ADRESS_BOOK 0x80000 /* address is in our address book */
74 #define MR_ORIGIN_MANUALLY_CREATED 0x100000 /* contact added by mrmailbox_create_contact() */
75 
76 #define MR_ORIGIN_MIN_CONTACT_LIST (MR_ORIGIN_INCOMING_REPLY_TO) /* contacts with at least this origin value are shown in the contact list */
77 #define MR_ORIGIN_MIN_VERIFIED (MR_ORIGIN_INCOMING_REPLY_TO) /* contacts with at least this origin value are verified and known not to be spam */
78 #define MR_ORIGIN_MIN_START_NEW_NCHAT (0x7FFFFFFF) /* contacts with at least this origin value start a new "normal" chat, defaults to off */
79 
80 
81 /* library-internal */
82 char* mrcontact_get_first_name (const char* full_name);
83 void mrcontact_normalize_name (char* full_name);
84 int mrcontact_load_from_db__ (mrcontact_t*, mrsqlite3_t*, uint32_t contact_id);
85 
86 
87 #ifdef __cplusplus
88 } /* /extern "C" */
89 #endif
90 #endif /* __MRCONTACT_H__ */
char * mrcontact_get_first_name(const char *full_name)
Get the first name.
Definition: mrcontact.c:99
+
An object representing a single contact in memory.
Definition: mrcontact.h:38
+
void mrcontact_empty(mrcontact_t *ths)
Empty a contact object.
Definition: mrcontact.c:65
+
mrcontact_t * mrcontact_new()
Create a new contact object in memory.
Definition: mrcontact.c:32
+
void mrcontact_unref(mrcontact_t *ths)
Free a contact object.
Definition: mrcontact.c:49
+
void mrcontact_normalize_name(char *full_name)
Normalize a name in-place.
Definition: mrcontact.c:130
+
char * m_authname
may be NULL or empty, this is the name authorized by the sender, only this name may be speaded to oth...
Definition: mrcontact.h:46
+
int m_blocked
Blocked state.
Definition: mrcontact.h:48
+
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
+
char * m_name
may be NULL or empty, this name should not be spreaded as it may be "Daddy" and so on; initially set ...
Definition: mrcontact.h:45
+
uint32_t m_id
The contact ID.
Definition: mrcontact.h:43
diff --git a/docs/user/html/mrdehtml_8h_source.html b/docs/html/mrdehtml_8h_source.html similarity index 99% rename from docs/user/html/mrdehtml_8h_source.html rename to docs/html/mrdehtml_8h_source.html index 105d010b..8416a511 100644 --- a/docs/user/html/mrdehtml_8h_source.html +++ b/docs/html/mrdehtml_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRDEHTML_H__
24 #define __MRDEHTML_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-internal *********************************************************/
31 
32 char* mr_dehtml(char* buf_terminated); /* mr_dehtml() returns way too many lineends; however, an optimisation on this issue is not needed as the lineends are typically remove in further processing by the caller */
33 
34 
35 #ifdef __cplusplus
36 } /* /extern "C" */
37 #endif
38 #endif /* __MRDEHTML_H__ */
39 
diff --git a/docs/user/html/mrimap_8h_source.html b/docs/html/mrimap_8h_source.html similarity index 99% rename from docs/user/html/mrimap_8h_source.html rename to docs/html/mrimap_8h_source.html index 5e7518ea..4c9e595f 100644 --- a/docs/user/html/mrimap_8h_source.html +++ b/docs/html/mrimap_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrimap.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Purpose: Reading from IMAP servers with no dependencies to the database.
24 mrmailbox_t is only used for logging and to get information about
25 the online state. */
26 
27 
28 #ifndef __MRIMAP_H__
29 #define __MRIMAP_H__
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 
35 typedef struct mrloginparam_t mrloginparam_t;
36 typedef struct mrimap_t mrimap_t;
37 
38 #define MR_IMAP_SEEN 0x0001L
39 
40 typedef int32_t (*mr_get_config_int_t)(mrimap_t*, const char*, int32_t);
41 typedef void (*mr_set_config_int_t)(mrimap_t*, const char*, int32_t);
42 typedef void (*mr_receive_imf_t) (mrimap_t*, const char* imf_raw_not_terminated, size_t imf_raw_bytes, const char* server_folder, uint32_t server_uid, uint32_t flags);
43 
44 
48 typedef struct mrimap_t
49 {
52  char* m_imap_server;
53  int m_imap_port;
54  char* m_imap_user;
55  char* m_imap_pw;
56  int m_server_flags;
57 
58  int m_connected; /* initally connected and watch thread installed */
59  mailimap* m_hEtpan; /* normally, if connected, m_hEtpan is also set; however, if a reconnection is required, we may lost this handle */
60  pthread_mutex_t m_hEtpanmutex;
61  int m_idle_set_up;
62  char* m_selected_folder;
63  int m_should_reconnect;
64 
65  int m_can_idle;
66  int m_has_xlist;
67  char* m_moveto_folder;/* Folder, where reveived chat messages should go to. Normally "Chats" but may be NULL to leave them in the INBOX */
68  char* m_sent_folder; /* Folder, where send messages should go to. Normally "Chats". */
69  pthread_mutex_t m_idlemutex; /* set, if idle is not possible; morover, the interrupted IDLE thread waits a second before IDLEing again; this allows several jobs to be executed */
70  pthread_mutex_t m_inwait_mutex; /* only used to wait for mailstream_wait_idle()/mailimap_idle_done() to terminate. */
71 
72  pthread_t m_watch_thread;
73  pthread_cond_t m_watch_cond;
74  pthread_mutex_t m_watch_condmutex;
75  int m_watch_condflag;
76  int m_watch_do_exit;
77 
78  time_t m_enter_watch_wait_time;
79 
80  pthread_t m_heartbeat_thread;
81  pthread_cond_t m_heartbeat_cond;
82  pthread_mutex_t m_heartbeat_condmutex;
83 
84  pthread_t m_restore_thread;
85  int m_restore_thread_created;
86  int m_restore_do_exit;
87 
88  struct mailimap_fetch_type* m_fetch_type_uid;
89  struct mailimap_fetch_type* m_fetch_type_body;
90  struct mailimap_fetch_type* m_fetch_type_flags;
91 
92  mr_get_config_int_t m_get_config_int;
93  mr_set_config_int_t m_set_config_int;
94  mr_receive_imf_t m_receive_imf;
95  void* m_userData;
96  mrmailbox_t* m_mailbox;
97 
98  int m_log_connect_errors;
99 } mrimap_t;
100 
101 
102 mrimap_t* mrimap_new (mr_get_config_int_t, mr_set_config_int_t, mr_receive_imf_t, void* userData, mrmailbox_t*);
103 void mrimap_unref (mrimap_t*);
104 
105 int mrimap_connect (mrimap_t*, const mrloginparam_t*);
106 void mrimap_disconnect (mrimap_t*);
107 int mrimap_is_connected (mrimap_t*);
108 int mrimap_fetch (mrimap_t*);
109 int mrimap_restore (mrimap_t*, time_t seconds_to_restore);
110 
111 int mrimap_append_msg (mrimap_t*, time_t timestamp, const char* data_not_terminated, size_t data_bytes, char** ret_server_folder, uint32_t* ret_server_uid);
112 
113 #define MR_MS_ALSO_MOVE 0x01
114 #define MR_MS_SET_MDNSent_FLAG 0x02
115 #define MR_MS_MDNSent_JUST_SET 0x10
116 int mrimap_markseen_msg (mrimap_t*, const char* folder, uint32_t server_uid, int ms_flags, char** ret_server_folder, uint32_t* ret_server_uid, int* ret_ms_flags); /* only returns 0 on connection problems; we should try later again in this case */
117 
118 int mrimap_delete_msg (mrimap_t*, const char* rfc724_mid, const char* folder, uint32_t server_uid); /* only returns 0 on connection problems; we should try later again in this case */
119 
120 void mrimap_heartbeat (mrimap_t*);
121 
122 #ifdef __cplusplus
123 } /* /extern "C" */
124 #endif
125 #endif /* __MRIMAP_H__ */
126 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Purpose: Reading from IMAP servers with no dependencies to the database.
24 mrmailbox_t is only used for logging and to get information about
25 the online state. */
26 
27 
28 #ifndef __MRIMAP_H__
29 #define __MRIMAP_H__
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 
35 typedef struct mrloginparam_t mrloginparam_t;
36 typedef struct mrimap_t mrimap_t;
37 
38 #define MR_IMAP_SEEN 0x0001L
39 
40 typedef int32_t (*mr_get_config_int_t)(mrimap_t*, const char*, int32_t);
41 typedef void (*mr_set_config_int_t)(mrimap_t*, const char*, int32_t);
42 typedef void (*mr_receive_imf_t) (mrimap_t*, const char* imf_raw_not_terminated, size_t imf_raw_bytes, const char* server_folder, uint32_t server_uid, uint32_t flags);
43 
44 
48 typedef struct mrimap_t
49 {
52  char* m_imap_server;
53  int m_imap_port;
54  char* m_imap_user;
55  char* m_imap_pw;
56  int m_server_flags;
57 
58  int m_connected; /* initally connected and watch thread installed */
59  mailimap* m_hEtpan; /* normally, if connected, m_hEtpan is also set; however, if a reconnection is required, we may lost this handle */
60  pthread_mutex_t m_hEtpanmutex;
61  int m_idle_set_up;
62  char* m_selected_folder;
63  int m_should_reconnect;
64 
65  int m_can_idle;
66  int m_has_xlist;
67  char* m_moveto_folder;/* Folder, where reveived chat messages should go to. Normally "Chats" but may be NULL to leave them in the INBOX */
68  char* m_sent_folder; /* Folder, where send messages should go to. Normally "Chats". */
69  pthread_mutex_t m_idlemutex; /* set, if idle is not possible; morover, the interrupted IDLE thread waits a second before IDLEing again; this allows several jobs to be executed */
70  pthread_mutex_t m_inwait_mutex; /* only used to wait for mailstream_wait_idle()/mailimap_idle_done() to terminate. */
71 
72  pthread_t m_watch_thread;
73  pthread_cond_t m_watch_cond;
74  pthread_mutex_t m_watch_condmutex;
75  int m_watch_condflag;
76  int m_watch_do_exit;
77 
78  time_t m_enter_watch_wait_time;
79 
80  pthread_t m_heartbeat_thread;
81  pthread_cond_t m_heartbeat_cond;
82  pthread_mutex_t m_heartbeat_condmutex;
83 
84  pthread_t m_restore_thread;
85  int m_restore_thread_created;
86  int m_restore_do_exit;
87 
88  struct mailimap_fetch_type* m_fetch_type_uid;
89  struct mailimap_fetch_type* m_fetch_type_body;
90  struct mailimap_fetch_type* m_fetch_type_flags;
91 
92  mr_get_config_int_t m_get_config_int;
93  mr_set_config_int_t m_set_config_int;
94  mr_receive_imf_t m_receive_imf;
95  void* m_userData;
96  mrmailbox_t* m_mailbox;
97 
98  int m_log_connect_errors;
99 } mrimap_t;
100 
101 
102 mrimap_t* mrimap_new (mr_get_config_int_t, mr_set_config_int_t, mr_receive_imf_t, void* userData, mrmailbox_t*);
103 void mrimap_unref (mrimap_t*);
104 
105 int mrimap_connect (mrimap_t*, const mrloginparam_t*);
106 void mrimap_disconnect (mrimap_t*);
107 int mrimap_is_connected (mrimap_t*);
108 int mrimap_fetch (mrimap_t*);
109 int mrimap_restore (mrimap_t*, time_t seconds_to_restore);
110 
111 int mrimap_append_msg (mrimap_t*, time_t timestamp, const char* data_not_terminated, size_t data_bytes, char** ret_server_folder, uint32_t* ret_server_uid);
112 
113 #define MR_MS_ALSO_MOVE 0x01
114 #define MR_MS_SET_MDNSent_FLAG 0x02
115 #define MR_MS_MDNSent_JUST_SET 0x10
116 int mrimap_markseen_msg (mrimap_t*, const char* folder, uint32_t server_uid, int ms_flags, char** ret_server_folder, uint32_t* ret_server_uid, int* ret_ms_flags); /* only returns 0 on connection problems; we should try later again in this case */
117 
118 int mrimap_delete_msg (mrimap_t*, const char* rfc724_mid, const char* folder, uint32_t server_uid); /* only returns 0 on connection problems; we should try later again in this case */
119 
120 void mrimap_heartbeat (mrimap_t*);
121 
122 #ifdef __cplusplus
123 } /* /extern "C" */
124 #endif
125 #endif /* __MRIMAP_H__ */
126 
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrjob_8h_source.html b/docs/html/mrjob_8h_source.html similarity index 97% rename from docs/user/html/mrjob_8h_source.html rename to docs/html/mrjob_8h_source.html index 675e7071..16c4f57c 100644 --- a/docs/user/html/mrjob_8h_source.html +++ b/docs/html/mrjob_8h_source.html @@ -88,12 +88,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrjob.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Contact: r10s@b44t.com, http://b44t.com
5  *
6  * This program is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program. If not, see http://www.gnu.org/licenses/ .
18  *
19  ******************************************************************************/
20 
21 
22 #ifndef __MRJOB_H__
23 #define __MRJOB_H__
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 
29 #define MRJ_DELETE_MSG_ON_IMAP 100 /* low priority ... */
30 #define MRJ_MARKSEEN_MDN_ON_IMAP 102
31 #define MRJ_SEND_MDN 105
32 #define MRJ_MARKSEEN_MSG_ON_IMAP 110
33 #define MRJ_SEND_MSG_TO_IMAP 700
34 #define MRJ_SEND_MSG_TO_SMTP 800
35 #define MRJ_CONNECT_TO_IMAP 900 /* ... high priority*/
36 
40 typedef struct mrjob_t
41 {
44  uint32_t m_job_id;
45  int m_action;
46  uint32_t m_foreign_id;
47  mrparam_t* m_param;
48  /* the following fields are set by the execution routines, m_param may also be modified */
49  time_t m_start_again_at; /* 1=on next loop, >1=on timestamp, 0=delete job (default) */
50 } mrjob_t;
51 
52 void mrjob_init_thread (mrmailbox_t*);
53 void mrjob_exit_thread (mrmailbox_t*);
54 uint32_t mrjob_add__ (mrmailbox_t*, int action, int foreign_id, const char* param); /* returns the job_id or 0 on errors. the job may or may not be done if the function returns. */
55 void mrjob_kill_action__ (mrmailbox_t*, int action); /* delete all pending jobs with the given action */
56 
57 #define MR_AT_ONCE 0
58 #define MR_INCREATION_POLL 2 /* this value does not increase the number of tries */
59 #define MR_STANDARD_DELAY 3
60 void mrjob_try_again_later (mrjob_t*, int initial_delay_seconds);
61 
62 
63 #ifdef __cplusplus
64 } /* /extern "C" */
65 #endif
66 #endif /* __MRJOB_H__ */
67 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Contact: r10s@b44t.com, http://b44t.com
5  *
6  * This program is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program. If not, see http://www.gnu.org/licenses/ .
18  *
19  ******************************************************************************/
20 
21 
22 #ifndef __MRJOB_H__
23 #define __MRJOB_H__
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 
29 #define MRJ_DELETE_MSG_ON_IMAP 100 /* low priority ... */
30 #define MRJ_MARKSEEN_MDN_ON_IMAP 102
31 #define MRJ_SEND_MDN 105
32 #define MRJ_MARKSEEN_MSG_ON_IMAP 110
33 #define MRJ_SEND_MSG_TO_IMAP 700
34 #define MRJ_SEND_MSG_TO_SMTP 800
35 #define MRJ_CONNECT_TO_IMAP 900 /* ... high priority*/
36 
40 typedef struct mrjob_t
41 {
44  uint32_t m_job_id;
45  int m_action;
46  uint32_t m_foreign_id;
47  mrparam_t* m_param;
48  /* the following fields are set by the execution routines, m_param may also be modified */
49  time_t m_start_again_at; /* 1=on next loop, >1=on timestamp, 0=delete job (default) */
50 } mrjob_t;
51 
52 void mrjob_init_thread (mrmailbox_t*);
53 void mrjob_exit_thread (mrmailbox_t*);
54 uint32_t mrjob_add__ (mrmailbox_t*, int action, int foreign_id, const char* param); /* returns the job_id or 0 on errors. the job may or may not be done if the function returns. */
55 void mrjob_kill_action__ (mrmailbox_t*, int action); /* delete all pending jobs with the given action */
56 
57 #define MR_AT_ONCE 0
58 #define MR_INCREATION_POLL 2 /* this value does not increase the number of tries */
59 #define MR_STANDARD_DELAY 3
60 void mrjob_try_again_later (mrjob_t*, int initial_delay_seconds);
61 
62 
63 #ifdef __cplusplus
64 } /* /extern "C" */
65 #endif
66 #endif /* __MRJOB_H__ */
67 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
An object for handling key=value parameter lists.
Definition: mrparam.h:36
diff --git a/docs/user/html/mrkey_8h_source.html b/docs/html/mrkey_8h_source.html similarity index 99% rename from docs/user/html/mrkey_8h_source.html rename to docs/html/mrkey_8h_source.html index 69a153e9..933aaf75 100644 --- a/docs/user/html/mrkey_8h_source.html +++ b/docs/html/mrkey_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrkey.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRKEY_H__
24 #define __MRKEY_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct sqlite3_stmt sqlite3_stmt;
32 
33 
34 #define MR_PUBLIC 0
35 #define MR_PRIVATE 1
36 
37 
41 typedef struct mrkey_t
42 {
43  void* m_binary;
44  int m_bytes;
45  int m_type;
46 
48  int _m_heap_refcnt; /* !=0 for objects created with mrkey_new(), 0 for stack objects */
49 } mrkey_t;
50 
51 
52 mrkey_t* mrkey_new ();
53 mrkey_t* mrkey_ref (mrkey_t*);
54 void mrkey_unref (mrkey_t*);
55 
56 int mrkey_set_from_raw (mrkey_t*, const void* data, int bytes, int type);
57 int mrkey_set_from_key (mrkey_t*, const mrkey_t*);
58 int mrkey_set_from_stmt (mrkey_t*, sqlite3_stmt*, int index, int type);
59 int mrkey_set_from_base64(mrkey_t*,const char* base64, int type);
60 int mrkey_set_from_file (mrkey_t*, const char* file, mrmailbox_t* mailbox);
61 
62 int mrkey_equals (const mrkey_t*, const mrkey_t*);
63 
64 int mrkey_save_self_keypair__(const mrkey_t* public_key, const mrkey_t* private_key, const char* addr, int is_default, mrsqlite3_t* sql);
65 int mrkey_load_self_public__ (mrkey_t*, const char* self_addr, mrsqlite3_t* sql);
66 int mrkey_load_self_private__(mrkey_t*, const char* self_addr, mrsqlite3_t* sql);
67 
68 char* mr_render_base64 (const void* buf, size_t buf_bytes, int break_every, const char* break_chars, int add_checksum); /* the result must be freed */
69 char* mrkey_render_base64(const mrkey_t* ths, int break_every, const char* break_chars, int add_checksum); /* the result must be freed */
70 char* mrkey_render_asc (const mrkey_t*, const char* add_header_lines); /* each header line must be terminated by \r\n, the result must be freed */
71 char* mrkey_render_fingerprint(const mrkey_t*, mrmailbox_t* mailbox);
72 char* mr_render_fingerprint(const uint8_t* data, size_t bytes);
73 void mr_wipe_secret_mem(void* buf, size_t buf_bytes);
74 
75 
76 #ifdef __cplusplus
77 } /* /extern "C" */
78 #endif
79 #endif /* __MRKEY_H__ */
80 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRKEY_H__
24 #define __MRKEY_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct sqlite3_stmt sqlite3_stmt;
32 
33 
34 #define MR_PUBLIC 0
35 #define MR_PRIVATE 1
36 
37 
41 typedef struct mrkey_t
42 {
43  void* m_binary;
44  int m_bytes;
45  int m_type;
46 
48  int _m_heap_refcnt; /* !=0 for objects created with mrkey_new(), 0 for stack objects */
49 } mrkey_t;
50 
51 
52 mrkey_t* mrkey_new ();
53 mrkey_t* mrkey_ref (mrkey_t*);
54 void mrkey_unref (mrkey_t*);
55 
56 int mrkey_set_from_raw (mrkey_t*, const void* data, int bytes, int type);
57 int mrkey_set_from_key (mrkey_t*, const mrkey_t*);
58 int mrkey_set_from_stmt (mrkey_t*, sqlite3_stmt*, int index, int type);
59 int mrkey_set_from_base64(mrkey_t*,const char* base64, int type);
60 int mrkey_set_from_file (mrkey_t*, const char* file, mrmailbox_t* mailbox);
61 
62 int mrkey_equals (const mrkey_t*, const mrkey_t*);
63 
64 int mrkey_save_self_keypair__(const mrkey_t* public_key, const mrkey_t* private_key, const char* addr, int is_default, mrsqlite3_t* sql);
65 int mrkey_load_self_public__ (mrkey_t*, const char* self_addr, mrsqlite3_t* sql);
66 int mrkey_load_self_private__(mrkey_t*, const char* self_addr, mrsqlite3_t* sql);
67 
68 char* mr_render_base64 (const void* buf, size_t buf_bytes, int break_every, const char* break_chars, int add_checksum); /* the result must be freed */
69 char* mrkey_render_base64(const mrkey_t* ths, int break_every, const char* break_chars, int add_checksum); /* the result must be freed */
70 char* mrkey_render_asc (const mrkey_t*, const char* add_header_lines); /* each header line must be terminated by \r\n, the result must be freed */
71 char* mrkey_render_fingerprint(const mrkey_t*, mrmailbox_t* mailbox);
72 char* mr_render_fingerprint(const uint8_t* data, size_t bytes);
73 void mr_wipe_secret_mem(void* buf, size_t buf_bytes);
74 
75 
76 #ifdef __cplusplus
77 } /* /extern "C" */
78 #endif
79 #endif /* __MRKEY_H__ */
80 
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrkeyring_8h_source.html b/docs/html/mrkeyring_8h_source.html similarity index 99% rename from docs/user/html/mrkeyring_8h_source.html rename to docs/html/mrkeyring_8h_source.html index f6963d72..84a44378 100644 --- a/docs/user/html/mrkeyring_8h_source.html +++ b/docs/html/mrkeyring_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRKEYRING_H__
24 #define __MRKEYRING_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrkey_t mrkey_t;
31 
32 
36 typedef struct mrkeyring_t
37 {
40  mrkey_t** m_keys;
41  int m_count;
42  int m_allocated;
43 } mrkeyring_t;
44 
45 mrkeyring_t* mrkeyring_new ();
46 void mrkeyring_unref();
47 
48 void mrkeyring_add (mrkeyring_t*, mrkey_t*); /* the reference counter of the key is increased by one */
49 
50 int mrkeyring_load_self_private_for_decrypting__(mrkeyring_t*, const char* self_addr, mrsqlite3_t* sql);
51 
52 
53 #ifdef __cplusplus
54 } /* /extern "C" */
55 #endif
56 #endif /* __MRKEYRING_H__ */
57 
diff --git a/docs/user/html/mrloginparam_8h_source.html b/docs/html/mrloginparam_8h_source.html similarity index 99% rename from docs/user/html/mrloginparam_8h_source.html rename to docs/html/mrloginparam_8h_source.html index 06085296..b5d6c572 100644 --- a/docs/user/html/mrloginparam_8h_source.html +++ b/docs/html/mrloginparam_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRLOGINPARAM_H__
24 #define __MRLOGINPARAM_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
33 typedef struct mrloginparam_t
34 {
37  /* IMAP/POP3 - all pointers may be NULL if unset, public read */
38  char* m_addr;
39  char* m_mail_server;
40  char* m_mail_user;
41  char* m_mail_pw;
42  uint16_t m_mail_port;
43 
44  /* SMTP - all pointers may be NULL if unset, public read */
45  char* m_send_server;
46  char* m_send_user;
47  char* m_send_pw;
48  int m_send_port;
49 
50  /* Server options*/
51  #define MR_AUTH_XOAUTH2 0x2
52  #define MR_AUTH_NORMAL 0x4
53  #define MR_AUTH_FLAGS (MR_AUTH_XOAUTH2|MR_AUTH_NORMAL) /* if none of these flags are set, the default is choosen, even if MR_NO_AUTOCONFIG is set */
54 
55  #define MR_IMAP_SOCKET_STARTTLS 0x100
56  #define MR_IMAP_SOCKET_SSL 0x200
57  #define MR_IMAP_SOCKET_PLAIN 0x400
58  #define MR_IMAP_SOCKET_FLAGS (MR_IMAP_SOCKET_STARTTLS|MR_IMAP_SOCKET_SSL|MR_IMAP_SOCKET_PLAIN) /* if none of these flags are set, the default is choosen, even if MR_NO_AUTOCONFIG is set */
59 
60  #define MR_SMTP_SOCKET_STARTTLS 0x10000
61  #define MR_SMTP_SOCKET_SSL 0x20000
62  #define MR_SMTP_SOCKET_PLAIN 0x40000
63  #define MR_SMTP_SOCKET_FLAGS (MR_SMTP_SOCKET_STARTTLS|MR_SMTP_SOCKET_SSL|MR_SMTP_SOCKET_PLAIN) /* if none of these flags are set, the default is choosen, even if MR_NO_AUTOCONFIG is set */
64 
65  #define MR_NO_EXTRA_IMAP_UPLOAD 0x2000000
66  #define MR_NO_MOVE_TO_CHATS 0x4000000
67 
68  int m_server_flags;
69 } mrloginparam_t;
70 
71 
72 mrloginparam_t* mrloginparam_new ();
73 void mrloginparam_unref (mrloginparam_t*);
74 void mrloginparam_empty (mrloginparam_t*); /* clears all data and frees its memory. All pointers are NULL after this function is called. */
75 void mrloginparam_read__ (mrloginparam_t*, mrsqlite3_t*, const char* prefix);
76 void mrloginparam_write__ (const mrloginparam_t*, mrsqlite3_t*, const char* prefix);
77 char* mrloginparam_get_readable (const mrloginparam_t*);
78 
79 
80 #ifdef __cplusplus
81 } /* /extern "C" */
82 #endif
83 #endif /* __MRLOGINPARAM_H__ */
84 
diff --git a/docs/user/html/mrmailbox_8h_source.html b/docs/html/mrmailbox_8h_source.html similarity index 93% rename from docs/user/html/mrmailbox_8h_source.html rename to docs/html/mrmailbox_8h_source.html index bf470526..577eeb08 100644 --- a/docs/user/html/mrmailbox_8h_source.html +++ b/docs/html/mrmailbox_8h_source.html @@ -88,80 +88,80 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrmailbox.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMAILBOX_H__
24 #define __MRMAILBOX_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #define MR_VERSION_MAJOR 0
31 #define MR_VERSION_MINOR 9
32 #define MR_VERSION_REVISION 7
33 
34 
100 #include <libetpan/libetpan.h> /* defines uint16_t etc. */
101 #include "mrchatlist.h"
102 #include "mrchat.h"
103 #include "mrmsg.h"
104 #include "mrcontact.h"
105 #include "mrpoortext.h"
106 #include "mrparam.h"
107 
108 typedef struct mrmailbox_t mrmailbox_t;
109 typedef struct mrimap_t mrimap_t;
110 typedef struct mrsmtp_t mrsmtp_t;
111 typedef struct mrsqlite3_t mrsqlite3_t;
112 typedef struct mrjob_t mrjob_t;
113 typedef struct mrmimeparser_t mrmimeparser_t;
114 
115 
131 typedef uintptr_t (*mrmailboxcb_t) (mrmailbox_t*, int event, uintptr_t data1, uintptr_t data2);
132 
133 
141 typedef struct mrmailbox_t
142 {
143  void* m_userdata;
144  char* m_dbfile;
145  char* m_blobdir;
148  mrsqlite3_t* m_sql;
149  mrimap_t* m_imap;
150  mrsmtp_t* m_smtp;
152  pthread_t m_job_thread;
153  pthread_cond_t m_job_cond;
154  pthread_mutex_t m_job_condmutex;
155  int m_job_condflag;
156  int m_job_do_exit;
158  mrmailboxcb_t m_cb;
160  char* m_os_name;
162  uint32_t m_cmdline_sel_chat_id;
164  int m_wake_lock;
165  pthread_mutex_t m_wake_lock_critical;
167  int m_e2ee_enabled;
169  #define MR_LOG_RINGBUF_SIZE 200
170  pthread_mutex_t m_log_ringbuf_critical;
171  char* m_log_ringbuf[MR_LOG_RINGBUF_SIZE];
172  time_t m_log_ringbuf_times[MR_LOG_RINGBUF_SIZE];
173  int m_log_ringbuf_pos;
175 } mrmailbox_t;
176 
177 
178 /* create/open/connect */
179 mrmailbox_t* mrmailbox_new (mrmailboxcb_t, void* userdata, const char* os_name);
180 void mrmailbox_unref (mrmailbox_t*);
181 
182 int mrmailbox_open (mrmailbox_t*, const char* dbfile, const char* blobdir);
183 void mrmailbox_close (mrmailbox_t*);
184 int mrmailbox_is_open (const mrmailbox_t*);
185 
186 int mrmailbox_set_config (mrmailbox_t*, const char* key, const char* value);
187 char* mrmailbox_get_config (mrmailbox_t*, const char* key, const char* def);
188 int mrmailbox_set_config_int (mrmailbox_t*, const char* key, int32_t value);
189 int32_t mrmailbox_get_config_int (mrmailbox_t*, const char* key, int32_t def);
190 char* mrmailbox_get_blobdir (mrmailbox_t*);
191 char* mrmailbox_get_version_str (void);
192 
193 void mrmailbox_configure_and_connect(mrmailbox_t*);
194 void mrmailbox_configure_cancel (mrmailbox_t*);
195 int mrmailbox_is_configured (mrmailbox_t*);
196 
197 void mrmailbox_connect (mrmailbox_t*);
198 void mrmailbox_disconnect (mrmailbox_t*);
199 
200 int mrmailbox_restore (mrmailbox_t*, time_t seconds_to_restore); /* not really implemented */
201 char* mrmailbox_get_info (mrmailbox_t*);
202 
203 
204 /* Handle chatlists */
205 #define MR_GCL_ARCHIVED_ONLY 0x01
206 #define MR_GCL_NO_SPECIALS 0x02
207 mrchatlist_t* mrmailbox_get_chatlist (mrmailbox_t*, int flags, const char* query);
208 
209 
210 /* Handle chats */
211 uint32_t mrmailbox_create_chat_by_contact_id (mrmailbox_t*, uint32_t contact_id);
212 uint32_t mrmailbox_get_chat_id_by_contact_id (mrmailbox_t*, uint32_t contact_id);
213 
214 uint32_t mrmailbox_send_text_msg (mrmailbox_t*, uint32_t chat_id, const char* text_to_send);
215 uint32_t mrmailbox_send_msg (mrmailbox_t*, uint32_t chat_id, mrmsg_t*);
216 void mrmailbox_set_draft (mrmailbox_t*, uint32_t chat_id, const char*);
217 
218 #define MR_GCM_ADDDAYMARKER 0x01
219 carray* mrmailbox_get_chat_msgs (mrmailbox_t*, uint32_t chat_id, uint32_t flags, uint32_t marker1before);
220 int mrmailbox_get_total_msg_count (mrmailbox_t*, uint32_t chat_id);
221 int mrmailbox_get_fresh_msg_count (mrmailbox_t*, uint32_t chat_id);
222 carray* mrmailbox_get_fresh_msgs (mrmailbox_t*);
223 void mrmailbox_marknoticed_chat (mrmailbox_t*, uint32_t chat_id);
224 carray* mrmailbox_get_chat_media (mrmailbox_t*, uint32_t chat_id, int msg_type, int or_msg_type);
225 uint32_t mrmailbox_get_next_media (mrmailbox_t*, uint32_t curr_msg_id, int dir);
226 
227 void mrmailbox_archive_chat (mrmailbox_t*, uint32_t chat_id, int archive);
228 void mrmailbox_delete_chat (mrmailbox_t*, uint32_t chat_id);
229 
230 carray* mrmailbox_get_chat_contacts (mrmailbox_t*, uint32_t chat_id);
231 carray* mrmailbox_search_msgs (mrmailbox_t*, uint32_t chat_id, const char* query);
232 
233 mrchat_t* mrmailbox_get_chat (mrmailbox_t*, uint32_t chat_id);
234 
235 
236 /* Handle group chats */
237 uint32_t mrmailbox_create_group_chat (mrmailbox_t*, const char* name);
238 int mrmailbox_is_contact_in_chat (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
239 int mrmailbox_add_contact_to_chat (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
240 int mrmailbox_remove_contact_from_chat (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
241 int mrmailbox_set_chat_name (mrmailbox_t*, uint32_t chat_id, const char* name);
242 int mrmailbox_set_chat_image (mrmailbox_t*, uint32_t chat_id, const char* image);
243 
244 
245 /* Handle messages */
246 char* mrmailbox_get_msg_info (mrmailbox_t*, uint32_t msg_id);
247 void mrmailbox_delete_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt);
248 void mrmailbox_forward_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt, uint32_t chat_id);
249 void mrmailbox_marknoticed_contact (mrmailbox_t*, uint32_t contact_id);
250 void mrmailbox_markseen_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt);
251 void mrmailbox_star_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt, int star);
252 mrmsg_t* mrmailbox_get_msg (mrmailbox_t*, uint32_t msg_id);
253 
254 
255 
256 /* Handle contacts */
257 uint32_t mrmailbox_create_contact (mrmailbox_t*, const char* name, const char* addr);
258 int mrmailbox_add_address_book (mrmailbox_t*, const char*);
259 carray* mrmailbox_get_known_contacts (mrmailbox_t*, const char* query);
260 int mrmailbox_get_blocked_count (mrmailbox_t*);
261 carray* mrmailbox_get_blocked_contacts (mrmailbox_t*);
262 void mrmailbox_block_contact (mrmailbox_t*, uint32_t contact_id, int block);
263 char* mrmailbox_get_contact_encrinfo (mrmailbox_t*, uint32_t contact_id);
264 int mrmailbox_delete_contact (mrmailbox_t*, uint32_t contact_id);
265 mrcontact_t* mrmailbox_get_contact (mrmailbox_t*, uint32_t contact_id);
266 
267 
268 /* logging */
269 void mrmailbox_log_error (mrmailbox_t*, int code, const char* msg, ...);
270 void mrmailbox_log_error_if (int* condition, mrmailbox_t*, int code, const char* msg, ...);
271 void mrmailbox_log_warning (mrmailbox_t*, int code, const char* msg, ...);
272 void mrmailbox_log_info (mrmailbox_t*, int code, const char* msg, ...);
273 void mrmailbox_log_vprintf (mrmailbox_t*, int event, int code, const char* msg, va_list);
274 int mrmailbox_get_thread_index (void);
275 
276 
277 /* library private */
278 uint32_t mrmailbox_send_msg_i__ (mrmailbox_t*, mrchat_t*, const mrmsg_t*, time_t);
279 void mrmailbox_connect_to_imap (mrmailbox_t*, mrjob_t*);
280 void mrmailbox_wake_lock (mrmailbox_t*);
281 void mrmailbox_wake_unlock (mrmailbox_t*);
282 int mrmailbox_poke_eml_file (mrmailbox_t*, const char* file);
283 int mrmailbox_is_reply_to_known_message__ (mrmailbox_t*, mrmimeparser_t*);
284 int mrmailbox_is_reply_to_messenger_message__ (mrmailbox_t*, mrmimeparser_t*);
285 time_t mrmailbox_correct_bad_timestamp__ (mrmailbox_t* ths, uint32_t chat_id, uint32_t from_id, time_t desired_timestamp, int is_fresh_msg);
286 void mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mrmailbox_t* ths, struct mailimf_mailbox_list* mb_list, int origin, carray* ids, int* check_self);
287 void mrmailbox_add_or_lookup_contacts_by_address_list__(mrmailbox_t* ths, struct mailimf_address_list* adr_list, int origin, carray* ids, int* check_self);
288 int mrmailbox_get_archived_count__ (mrmailbox_t*);
289 int mrmailbox_reset_tables (mrmailbox_t*, int bits); /* reset tables but leaves server configuration, 1=jobs, 2=e2ee, 8=rest but server config */
290 size_t mrmailbox_get_real_contact_cnt__ (mrmailbox_t*);
291 uint32_t mrmailbox_add_or_lookup_contact__ (mrmailbox_t*, const char* display_name /*can be NULL*/, const char* addr_spec, int origin, int* sth_modified);
292 int mrmailbox_get_contact_origin__ (mrmailbox_t*, uint32_t id, int* ret_blocked);
293 int mrmailbox_is_contact_blocked__ (mrmailbox_t*, uint32_t id);
294 int mrmailbox_real_contact_exists__ (mrmailbox_t*, uint32_t id);
295 int mrmailbox_contact_addr_equals__ (mrmailbox_t*, uint32_t contact_id, const char* other_addr);
296 void mrmailbox_scaleup_contact_origin__ (mrmailbox_t*, uint32_t contact_id, int origin);
297 
298 
299 /* library private: end-to-end-encryption */
300 #define MR_E2EE_DEFAULT_ENABLED 1
301 #define MR_MDNS_DEFAULT_ENABLED 1
302 
303 typedef struct mrmailbox_e2ee_helper_t {
304  int m_encryption_successfull;
305  void* m_cdata_to_free;
306 } mrmailbox_e2ee_helper_t;
307 
308 void mrmailbox_e2ee_encrypt (mrmailbox_t*, const clist* recipients_addr, int e2ee_guaranteed, int encrypt_to_self, struct mailmime* in_out_message, mrmailbox_e2ee_helper_t*);
309 int mrmailbox_e2ee_decrypt (mrmailbox_t*, struct mailmime* in_out_message, int* ret_validation_errors); /* returns 1 if sth. was decrypted, 0 in other cases */
310 void mrmailbox_e2ee_thanks (mrmailbox_e2ee_helper_t*); /* frees data referenced by "mailmime" but not freed by mailmime_free(). After calling mre2ee_unhelp(), in_out_message cannot be used any longer! */
311 int mrmailbox_ensure_secret_key_exists (mrmailbox_t*); /* makes sure, the private key exists, needed only for exporting keys and the case no message was sent before */
312 
313 
314 /*******************************************************************************
315  * Events
316  ******************************************************************************/
317 
318 
319 /* The following events may be passed to the callback given to mrmailbox_new() */
320 
321 
322 /* Information, should not be reported, can be logged,
323 data1=0, data2=info string */
324 #define MR_EVENT_INFO 100
325 
326 
327 /* Warning, should not be reported, should be logged
328 data1=0, data2=warning string */
329 #define MR_EVENT_WARNING 300
330 
331 
332 /* Error, must be reported to the user by a non-disturbing bubble or so.
333 data1=error code MR_ERR_*, see below, data2=error string */
334 #define MR_EVENT_ERROR 400
335 
336 
337 /* one or more messages changed for some reasons in the database - added or
338 removed. For added messages: data1=chat_id, data2=msg_id */
339 #define MR_EVENT_MSGS_CHANGED 2000
340 
341 
342 /* For fresh messages from the INBOX, MR_EVENT_INCOMING_MSG is send;
343 data1=chat_id, data2=msg_id */
344 #define MR_EVENT_INCOMING_MSG 2005
345 
346 
347 /* a single message is send successfully (state changed from PENDING/SENDING to
348 DELIVERED); data1=chat_id, data2=msg_id */
349 #define MR_EVENT_MSG_DELIVERED 2010
350 
351 
352 /* a single message is read by the receiver (state changed from DELIVERED to
353 READ); data1=chat_id, data2=msg_id */
354 #define MR_EVENT_MSG_READ 2015
355 
356 
357 /* group name/image changed or members added/removed */
358 #define MR_EVENT_CHAT_MODIFIED 2020
359 
360 
361 /* contact(s) created, renamed, blocked or deleted */
362 #define MR_EVENT_CONTACTS_CHANGED 2030
363 
364 
365 /* connection state changed,
366 data1=0:failed-not-connected, 1:configured-and-connected */
367 #define MR_EVENT_CONFIGURE_ENDED 2040
368 
369 
370 /* data1=percent */
371 #define MR_EVENT_CONFIGURE_PROGRESS 2041
372 
373 
374 /* mrmailbox_imex() done:
375 data1=0:failed, 1=success */
376 #define MR_EVENT_IMEX_ENDED 2050
377 
378 
379 /* data1=permille */
380 #define MR_EVENT_IMEX_PROGRESS 2051
381 
382 
383 /* file written, event may be needed to make the file public to some system
384 services. data1=file name, data2=mime type */
385 #define MR_EVENT_IMEX_FILE_WRITTEN 2052
386 
387 
388 /* The following events are functions that should be provided by the frontends */
389 
390 
391 /* check, if the system is online currently
392 ret=0: not online, ret=1: online */
393 #define MR_EVENT_IS_ONLINE 2080
394 
395 
396 /* get a string from the frontend, data1=MR_STR_*, ret=string which will be
397 free()'d by the backend */
398 #define MR_EVENT_GET_STRING 2091
399 
400 
401 /* synchronous http/https(!) call, data1=url, ret=content which will be
402 free()'d by the backend, 0 on errors */
403 #define MR_EVENT_GET_QUANTITY_STRING 2092
404 
405 
406 /* synchronous http/https(!) call, data1=url, ret=content which will be free()'d
407 by the backend, 0 on errors */
408 #define MR_EVENT_HTTP_GET 2100
409 
410 /* acquire wakeLock (data1=1) or release it (data1=0), the backend does not make
411 nested or unsynchronized calls */
412 #define MR_EVENT_WAKE_LOCK 2110
413 
414 
415 /* Error codes */
416 #define MR_ERR_SELF_NOT_IN_GROUP 1
417 #define MR_ERR_NONETWORK 2
418 
419 
420 /*******************************************************************************
421  * Import/export and Tools
422  ******************************************************************************/
423 
424 
425 #define MR_IMEX_CANCEL 0
426 #define MR_IMEX_EXPORT_SELF_KEYS 1
427 #define MR_IMEX_IMPORT_SELF_KEYS 2
428 #define MR_IMEX_EXPORT_BACKUP 11
429 #define MR_IMEX_IMPORT_BACKUP 12
430 #define MR_IMEX_EXPORT_SETUP_MESSAGE 20
431 #define MR_BAK_PREFIX "delta-chat"
432 #define MR_BAK_SUFFIX "bak"
433 void mrmailbox_imex (mrmailbox_t*, int what, const char* param1, const char* setup_code);
434 char* mrmailbox_imex_has_backup (mrmailbox_t*, const char* dir);
435 
436 
437 
438 int mrmailbox_check_password (mrmailbox_t*, const char* pw);
439 
440 
441 
442 char* mrmailbox_create_setup_code (mrmailbox_t*);
443 
444 
445 
446 int mrmailbox_poke_spec (mrmailbox_t*, const char* spec);
447 
448 
449 /* The library tries itself to stay alive. For this purpose there is an additional
450 "heartbeat" thread that checks if the IDLE-thread is up and working. This check is done about every minute.
451 However, depending on the operating system, this thread may be delayed or stopped, if this is the case you can
452 force additional checks manually by just calling mrmailbox_heartbeat() about every minute.
453 If in doubt, call this function too often, not too less :-) */
454 void mrmailbox_heartbeat (mrmailbox_t*);
455 
456 
457 /* carray tools, already defined are things as
458 unsigned unt carray_count() */
459 uint32_t carray_get_uint32 (carray*, unsigned int index);
460 
461 
462 /* deprecated functions */
463 mrchat_t* mrchatlist_get_chat_by_index (mrchatlist_t*, size_t index); /* deprecated - use mrchatlist_get_chat_id_by_index() */
464 mrmsg_t* mrchatlist_get_msg_by_index (mrchatlist_t*, size_t index); /* deprecated - use mrchatlist_get_msg_id_by_index() */
465 int mrchat_set_draft (mrchat_t*, const char* msg); /* deprecated - use mrmailbox_set_draft() instead */
466 
467 
468 /* library-internal */
469 void mrmailbox_unarchive_chat__ (mrmailbox_t*, uint32_t chat_id);
470 size_t mrmailbox_get_chat_cnt__ (mrmailbox_t*);
471 uint32_t mrmailbox_create_or_lookup_nchat_by_contact_id__(mrmailbox_t*, uint32_t contact_id);
472 uint32_t mrmailbox_lookup_real_nchat_by_contact_id__(mrmailbox_t*, uint32_t contact_id);
473 int mrmailbox_get_total_msg_count__ (mrmailbox_t*, uint32_t chat_id);
474 int mrmailbox_get_fresh_msg_count__ (mrmailbox_t*, uint32_t chat_id);
475 uint32_t mrmailbox_get_last_deaddrop_fresh_msg__(mrmailbox_t*);
476 void mrmailbox_send_msg_to_smtp (mrmailbox_t*, mrjob_t*);
477 void mrmailbox_send_msg_to_imap (mrmailbox_t*, mrjob_t*);
478 int mrmailbox_add_contact_to_chat__ (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
479 int mrmailbox_is_contact_in_chat__ (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
480 int mrmailbox_get_chat_contact_count__ (mrmailbox_t*, uint32_t chat_id);
481 int mrmailbox_group_explicitly_left__ (mrmailbox_t*, const char* grpid);
482 void mrmailbox_set_group_explicitly_left__ (mrmailbox_t*, const char* grpid);
483 
484 size_t mrmailbox_get_real_msg_cnt__ (mrmailbox_t*); /* the number of messages assigned to real chat (!=deaddrop, !=trash) */
485 size_t mrmailbox_get_deaddrop_msg_cnt__ (mrmailbox_t*);
486 int mrmailbox_rfc724_mid_cnt__ (mrmailbox_t*, const char* rfc724_mid);
487 int mrmailbox_rfc724_mid_exists__ (mrmailbox_t*, const char* rfc724_mid, char** ret_server_folder, uint32_t* ret_server_uid);
488 void mrmailbox_update_server_uid__ (mrmailbox_t*, const char* rfc724_mid, const char* server_folder, uint32_t server_uid);
489 void mrmailbox_update_msg_chat_id__ (mrmailbox_t*, uint32_t msg_id, uint32_t chat_id);
490 void mrmailbox_update_msg_state__ (mrmailbox_t*, uint32_t msg_id, int state);
491 void mrmailbox_delete_msg_on_imap (mrmailbox_t* mailbox, mrjob_t* job);
492 int mrmailbox_mdn_from_ext__ (mrmailbox_t*, uint32_t from_id, const char* rfc724_mid, uint32_t* ret_chat_id, uint32_t* ret_msg_id); /* returns 1 if an event should be send */
493 void mrmailbox_send_mdn (mrmailbox_t*, mrjob_t* job);
494 void mrmailbox_markseen_msg_on_imap (mrmailbox_t* mailbox, mrjob_t* job);
495 void mrmailbox_markseen_mdn_on_imap (mrmailbox_t* mailbox, mrjob_t* job);
496 
497 
498 #ifdef __cplusplus
499 } /* /extern "C" */
500 #endif
501 #endif /* __MRMAILBOX_H__ */
int mrmailbox_add_address_book(mrmailbox_t *mailbox, const char *adr_book)
Add a number of contacts.
Definition: mrmailbox.c:4021
-
uint32_t mrmailbox_create_group_chat(mrmailbox_t *mailbox, const char *chat_name)
Create a new group chat.
Definition: mrmailbox.c:3333
-
uint32_t mrmailbox_create_contact(mrmailbox_t *mailbox, const char *name, const char *addr)
Add a single contact.
Definition: mrmailbox.c:3985
-
uint32_t mrmailbox_send_text_msg(mrmailbox_t *mailbox, uint32_t chat_id, const char *text_to_send)
Send a simple text message to the given chat.
Definition: mrmailbox.c:3101
-
void mrmailbox_close(mrmailbox_t *mailbox)
Close mailbox database.
Definition: mrmailbox.c:1075
-
mrmailbox_t * mrmailbox_new(mrmailboxcb_t cb, void *userdata, const char *os_name)
Create a new mailbox object.
Definition: mrmailbox.c:897
-
int mrmailbox_is_contact_in_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Check if a given contact ID is a member of a group chat.
Definition: mrmailbox.c:3565
-
mrchat_t * mrmailbox_get_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Get a chat object of type mrchat_t by a chat_id.
Definition: mrmailbox.c:1709
-
int mrmailbox_remove_contact_from_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Remove a member from a group.
Definition: mrmailbox.c:3678
-
int mrmailbox_open(mrmailbox_t *mailbox, const char *dbfile, const char *blobdir)
Open mailbox database.
Definition: mrmailbox.c:1010
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
int mrmailbox_delete_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Delete a contact.
Definition: mrmailbox.c:4509
-
An object representing a single chatlist in memory.
Definition: mrchatlist.h:42
-
carray * mrmailbox_get_chat_msgs(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t flags, uint32_t marker1before)
Get all message IDs belonging to a chat.
Definition: mrmailbox.c:2120
-
void mrmailbox_star_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt, int star)
Star/unstar messages by setting the last parameter to 0 (unstar) or 1(star).
Definition: mrmailbox.c:4954
-
carray * mrmailbox_search_msgs(mrmailbox_t *mailbox, uint32_t chat_id, const char *query)
Search messages containing the given query string.
Definition: mrmailbox.c:2224
-
int mrmailbox_set_config(mrmailbox_t *ths, const char *key, const char *value)
Configure the mailbox.
Definition: mrmailbox.c:1176
-
char * m_dbfile
the database file in file.
Definition: mrmailbox.h:144
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrmailbox_delete_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt)
Delete a list of messages.
Definition: mrmailbox.c:5096
-
int mrmailbox_is_open(const mrmailbox_t *mailbox)
Check if a given mailbox database is open.
Definition: mrmailbox.c:1109
-
void mrmailbox_archive_chat(mrmailbox_t *mailbox, uint32_t chat_id, int archive)
Archive or unarchive a chat.
Definition: mrmailbox.c:2652
-
char * mrmailbox_get_info(mrmailbox_t *mailbox)
Get information about the mailbox.
Definition: mrmailbox.c:1292
-
int mrmailbox_add_contact_to_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Add a member to a group.
Definition: mrmailbox.c:3595
-
uint32_t mrmailbox_get_next_media(mrmailbox_t *mailbox, uint32_t curr_msg_id, int dir)
Returns all message IDs of the given types in a chat.
Definition: mrmailbox.c:1927
-
char * mrmailbox_get_version_str(void)
Find out the version of the Delta Chat core library.
Definition: mrmailbox.c:1490
-
void mrmailbox_disconnect(mrmailbox_t *mailbox)
Disonnect the mailbox from the server.
Definition: mrmailbox.c:1605
-
char * m_blobdir
full path of the blob directory in use.
Definition: mrmailbox.h:145
-
carray * mrmailbox_get_fresh_msgs(mrmailbox_t *mailbox)
Returns the message IDs of all fresh messages of any chat.
Definition: mrmailbox.c:2052
-
uint32_t mrmailbox_send_msg(mrmailbox_t *mailbox, uint32_t chat_id, mrmsg_t *msg)
save message in database and send it, the given message object is not unref&#39;d by the function but som...
Definition: mrmailbox.c:3142
-
void mrmailbox_connect(mrmailbox_t *mailbox)
Connect to the mailbox using the configured settings.
Definition: mrmailbox.c:1578
-
int mrmailbox_get_total_msg_count(mrmailbox_t *mailbox, uint32_t chat_id)
Get the total number of messages in a chat.
Definition: mrmailbox.c:2588
-
carray * mrmailbox_get_chat_contacts(mrmailbox_t *mailbox, uint32_t chat_id)
Get contact IDs belonging to a chat.
Definition: mrmailbox.c:2003
-
void mrmailbox_unref(mrmailbox_t *mailbox)
Free a mailbox object.
Definition: mrmailbox.c:954
-
void mrmailbox_block_contact(mrmailbox_t *mailbox, uint32_t contact_id, int new_blocking)
Block or unblock a contact.
Definition: mrmailbox.c:4290
-
uintptr_t(* mrmailboxcb_t)(mrmailbox_t *, int event, uintptr_t data1, uintptr_t data2)
Callback function that should be given to mrmailbox_new().
Definition: mrmailbox.h:131
-
mrchatlist_t * mrmailbox_get_chatlist(mrmailbox_t *mailbox, int listflags, const char *query)
Get a list of chats.
Definition: mrmailbox.c:1663
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
void mrmailbox_configure_and_connect(mrmailbox_t *mailbox)
Configure and connect a mailbox.
-
uint32_t mrmailbox_get_chat_id_by_contact_id(mrmailbox_t *mailbox, uint32_t contact_id)
Check, if there is a normal chat with a given contact.
Definition: mrmailbox.c:1789
-
int mrmailbox_get_fresh_msg_count(mrmailbox_t *mailbox, uint32_t chat_id)
Get the number of fresh messages in a chat.
Definition: mrmailbox.c:2616
-
void mrmailbox_delete_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Delete a chat:
Definition: mrmailbox.c:2773
-
void mrmailbox_set_draft(mrmailbox_t *mailbox, uint32_t chat_id, const char *msg)
Save a draft for a chat.
Definition: mrmailbox.c:2371
-
int mrmailbox_get_blocked_count(mrmailbox_t *mailbox)
Get the number of blocked contacts.
Definition: mrmailbox.c:4174
-
int mrmailbox_set_chat_image(mrmailbox_t *mailbox, uint32_t chat_id, const char *new_image)
Set group image.
Definition: mrmailbox.c:3476
-
carray * mrmailbox_get_known_contacts(mrmailbox_t *mailbox, const char *query)
Returns known and unblocked contacts.
Definition: mrmailbox.c:4076
-
char * mrmailbox_get_blobdir(mrmailbox_t *mailbox)
Get the blob directory.
Definition: mrmailbox.c:1276
-
mrmsg_t * mrmailbox_get_msg(mrmailbox_t *mailbox, uint32_t msg_id)
Get a single message object of the type mrmsg_t.
Definition: mrmailbox.c:4701
-
void * m_userdata
the same pointer as given to mrmailbox_new(), may be used by the caller for any purpose ...
Definition: mrmailbox.h:143
-
int mrmailbox_is_configured(mrmailbox_t *mailbox)
Check if the mailbox is already configured.
-
carray * mrmailbox_get_chat_media(mrmailbox_t *mailbox, uint32_t chat_id, int msg_type, int or_msg_type)
Returns all message IDs of the given types in a chat.
Definition: mrmailbox.c:1896
-
uint32_t mrmailbox_create_chat_by_contact_id(mrmailbox_t *mailbox, uint32_t contact_id)
Create a normal chat with a single user.
Definition: mrmailbox.c:1816
-
void mrmailbox_forward_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt, uint32_t chat_id)
Forward a list of messages to another chat.
Definition: mrmailbox.c:4868
-
void mrmailbox_markseen_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt)
Mark a message as seen, updates the IMAP state and sends MDNs.
Definition: mrmailbox.c:5237
-
void mrmailbox_marknoticed_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Mark all message in a chat as noticed.
Definition: mrmailbox.c:1755
-
char * mrmailbox_imex_has_backup(mrmailbox_t *mailbox, const char *dir_name)
Check if there is a backup file.
-
void mrmailbox_configure_cancel(mrmailbox_t *mailbox)
Cancel an configuration started by mrmailbox_configure_and_connect().
-
carray * mrmailbox_get_blocked_contacts(mrmailbox_t *mailbox)
Get blocked contacts.
Definition: mrmailbox.c:4140
-
mrcontact_t * mrmailbox_get_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Get a single contact object.
Definition: mrmailbox.c:4216
-
int mrmailbox_check_password(mrmailbox_t *mailbox, const char *test_pw)
Check if the user is authorized by the given password in some way.
-
void mrmailbox_marknoticed_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Mark all messages send by the given contact as noticed.
Definition: mrmailbox.c:4264
-
int mrmailbox_set_config_int(mrmailbox_t *ths, const char *key, int32_t value)
Configure the mailbox.
Definition: mrmailbox.c:1229
-
int32_t mrmailbox_get_config_int(mrmailbox_t *ths, const char *key, int32_t def)
Get a configuration option.
Definition: mrmailbox.c:1251
-
int mrmailbox_set_chat_name(mrmailbox_t *mailbox, uint32_t chat_id, const char *new_name)
Set group name.
Definition: mrmailbox.c:3399
-
char * mrmailbox_create_setup_code(mrmailbox_t *mailbox)
Create random setup code.
-
char * mrmailbox_get_contact_encrinfo(mrmailbox_t *mailbox, uint32_t contact_id)
Get encryption info.
Definition: mrmailbox.c:4381
-
void mrmailbox_imex(mrmailbox_t *mailbox, int what, const char *param1, const char *setup_code)
Import/export things.
-
char * mrmailbox_get_config(mrmailbox_t *ths, const char *key, const char *def)
Get a configuration option.
Definition: mrmailbox.c:1207
-
char * mrmailbox_get_msg_info(mrmailbox_t *mailbox, uint32_t msg_id)
Get an informational text for a single message.
Definition: mrmailbox.c:4743
-
An object representing a single chat in memory.
Definition: mrchat.h:39
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMAILBOX_H__
24 #define __MRMAILBOX_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #define MR_VERSION_MAJOR 0
31 #define MR_VERSION_MINOR 9
32 #define MR_VERSION_REVISION 7
33 
34 
100 #include <libetpan/libetpan.h> /* defines uint16_t etc. */
101 #include "mrchatlist.h"
102 #include "mrchat.h"
103 #include "mrmsg.h"
104 #include "mrcontact.h"
105 #include "mrpoortext.h"
106 #include "mrparam.h"
107 
108 typedef struct mrmailbox_t mrmailbox_t;
109 typedef struct mrimap_t mrimap_t;
110 typedef struct mrsmtp_t mrsmtp_t;
111 typedef struct mrsqlite3_t mrsqlite3_t;
112 typedef struct mrjob_t mrjob_t;
113 typedef struct mrmimeparser_t mrmimeparser_t;
114 
115 
131 typedef uintptr_t (*mrmailboxcb_t) (mrmailbox_t*, int event, uintptr_t data1, uintptr_t data2);
132 
133 
141 typedef struct mrmailbox_t
142 {
143  void* m_userdata;
144  char* m_dbfile;
145  char* m_blobdir;
148  mrsqlite3_t* m_sql;
149  mrimap_t* m_imap;
150  mrsmtp_t* m_smtp;
152  pthread_t m_job_thread;
153  pthread_cond_t m_job_cond;
154  pthread_mutex_t m_job_condmutex;
155  int m_job_condflag;
156  int m_job_do_exit;
158  mrmailboxcb_t m_cb;
160  char* m_os_name;
162  uint32_t m_cmdline_sel_chat_id;
164  int m_wake_lock;
165  pthread_mutex_t m_wake_lock_critical;
167  int m_e2ee_enabled;
169  #define MR_LOG_RINGBUF_SIZE 200
170  pthread_mutex_t m_log_ringbuf_critical;
171  char* m_log_ringbuf[MR_LOG_RINGBUF_SIZE];
172  time_t m_log_ringbuf_times[MR_LOG_RINGBUF_SIZE];
173  int m_log_ringbuf_pos;
175 } mrmailbox_t;
176 
177 
178 /* create/open/connect */
179 mrmailbox_t* mrmailbox_new (mrmailboxcb_t, void* userdata, const char* os_name);
180 void mrmailbox_unref (mrmailbox_t*);
181 
182 int mrmailbox_open (mrmailbox_t*, const char* dbfile, const char* blobdir);
183 void mrmailbox_close (mrmailbox_t*);
184 int mrmailbox_is_open (const mrmailbox_t*);
185 
186 int mrmailbox_set_config (mrmailbox_t*, const char* key, const char* value);
187 char* mrmailbox_get_config (mrmailbox_t*, const char* key, const char* def);
188 int mrmailbox_set_config_int (mrmailbox_t*, const char* key, int32_t value);
189 int32_t mrmailbox_get_config_int (mrmailbox_t*, const char* key, int32_t def);
190 char* mrmailbox_get_blobdir (mrmailbox_t*);
191 char* mrmailbox_get_version_str (void);
192 
193 void mrmailbox_configure_and_connect(mrmailbox_t*);
194 void mrmailbox_configure_cancel (mrmailbox_t*);
195 int mrmailbox_is_configured (mrmailbox_t*);
196 
197 void mrmailbox_connect (mrmailbox_t*);
198 void mrmailbox_disconnect (mrmailbox_t*);
199 
200 int mrmailbox_restore (mrmailbox_t*, time_t seconds_to_restore); /* not really implemented */
201 char* mrmailbox_get_info (mrmailbox_t*);
202 
203 
204 /* Handle chatlists */
205 #define MR_GCL_ARCHIVED_ONLY 0x01
206 #define MR_GCL_NO_SPECIALS 0x02
207 mrchatlist_t* mrmailbox_get_chatlist (mrmailbox_t*, int flags, const char* query);
208 
209 
210 /* Handle chats */
211 uint32_t mrmailbox_create_chat_by_contact_id (mrmailbox_t*, uint32_t contact_id);
212 uint32_t mrmailbox_get_chat_id_by_contact_id (mrmailbox_t*, uint32_t contact_id);
213 
214 uint32_t mrmailbox_send_text_msg (mrmailbox_t*, uint32_t chat_id, const char* text_to_send);
215 uint32_t mrmailbox_send_msg (mrmailbox_t*, uint32_t chat_id, mrmsg_t*);
216 void mrmailbox_set_draft (mrmailbox_t*, uint32_t chat_id, const char*);
217 
218 #define MR_GCM_ADDDAYMARKER 0x01
219 carray* mrmailbox_get_chat_msgs (mrmailbox_t*, uint32_t chat_id, uint32_t flags, uint32_t marker1before);
220 int mrmailbox_get_total_msg_count (mrmailbox_t*, uint32_t chat_id);
221 int mrmailbox_get_fresh_msg_count (mrmailbox_t*, uint32_t chat_id);
222 carray* mrmailbox_get_fresh_msgs (mrmailbox_t*);
223 void mrmailbox_marknoticed_chat (mrmailbox_t*, uint32_t chat_id);
224 carray* mrmailbox_get_chat_media (mrmailbox_t*, uint32_t chat_id, int msg_type, int or_msg_type);
225 uint32_t mrmailbox_get_next_media (mrmailbox_t*, uint32_t curr_msg_id, int dir);
226 
227 void mrmailbox_archive_chat (mrmailbox_t*, uint32_t chat_id, int archive);
228 void mrmailbox_delete_chat (mrmailbox_t*, uint32_t chat_id);
229 
230 carray* mrmailbox_get_chat_contacts (mrmailbox_t*, uint32_t chat_id);
231 carray* mrmailbox_search_msgs (mrmailbox_t*, uint32_t chat_id, const char* query);
232 
233 mrchat_t* mrmailbox_get_chat (mrmailbox_t*, uint32_t chat_id);
234 
235 
236 /* Handle group chats */
237 uint32_t mrmailbox_create_group_chat (mrmailbox_t*, const char* name);
238 int mrmailbox_is_contact_in_chat (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
239 int mrmailbox_add_contact_to_chat (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
240 int mrmailbox_remove_contact_from_chat (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
241 int mrmailbox_set_chat_name (mrmailbox_t*, uint32_t chat_id, const char* name);
242 int mrmailbox_set_chat_image (mrmailbox_t*, uint32_t chat_id, const char* image);
243 
244 
245 /* Handle messages */
246 char* mrmailbox_get_msg_info (mrmailbox_t*, uint32_t msg_id);
247 void mrmailbox_delete_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt);
248 void mrmailbox_forward_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt, uint32_t chat_id);
249 void mrmailbox_marknoticed_contact (mrmailbox_t*, uint32_t contact_id);
250 void mrmailbox_markseen_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt);
251 void mrmailbox_star_msgs (mrmailbox_t*, const uint32_t* msg_ids, int msg_cnt, int star);
252 mrmsg_t* mrmailbox_get_msg (mrmailbox_t*, uint32_t msg_id);
253 
254 
255 
256 /* Handle contacts */
257 uint32_t mrmailbox_create_contact (mrmailbox_t*, const char* name, const char* addr);
258 int mrmailbox_add_address_book (mrmailbox_t*, const char*);
259 carray* mrmailbox_get_known_contacts (mrmailbox_t*, const char* query);
260 int mrmailbox_get_blocked_count (mrmailbox_t*);
261 carray* mrmailbox_get_blocked_contacts (mrmailbox_t*);
262 void mrmailbox_block_contact (mrmailbox_t*, uint32_t contact_id, int block);
263 char* mrmailbox_get_contact_encrinfo (mrmailbox_t*, uint32_t contact_id);
264 int mrmailbox_delete_contact (mrmailbox_t*, uint32_t contact_id);
265 mrcontact_t* mrmailbox_get_contact (mrmailbox_t*, uint32_t contact_id);
266 
267 
268 /* logging */
269 void mrmailbox_log_error (mrmailbox_t*, int code, const char* msg, ...);
270 void mrmailbox_log_error_if (int* condition, mrmailbox_t*, int code, const char* msg, ...);
271 void mrmailbox_log_warning (mrmailbox_t*, int code, const char* msg, ...);
272 void mrmailbox_log_info (mrmailbox_t*, int code, const char* msg, ...);
273 void mrmailbox_log_vprintf (mrmailbox_t*, int event, int code, const char* msg, va_list);
274 int mrmailbox_get_thread_index (void);
275 
276 
277 /* library private */
278 uint32_t mrmailbox_send_msg_i__ (mrmailbox_t*, mrchat_t*, const mrmsg_t*, time_t);
279 void mrmailbox_connect_to_imap (mrmailbox_t*, mrjob_t*);
280 void mrmailbox_wake_lock (mrmailbox_t*);
281 void mrmailbox_wake_unlock (mrmailbox_t*);
282 int mrmailbox_poke_eml_file (mrmailbox_t*, const char* file);
283 int mrmailbox_is_reply_to_known_message__ (mrmailbox_t*, mrmimeparser_t*);
284 int mrmailbox_is_reply_to_messenger_message__ (mrmailbox_t*, mrmimeparser_t*);
285 time_t mrmailbox_correct_bad_timestamp__ (mrmailbox_t* ths, uint32_t chat_id, uint32_t from_id, time_t desired_timestamp, int is_fresh_msg);
286 void mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mrmailbox_t* ths, struct mailimf_mailbox_list* mb_list, int origin, carray* ids, int* check_self);
287 void mrmailbox_add_or_lookup_contacts_by_address_list__(mrmailbox_t* ths, struct mailimf_address_list* adr_list, int origin, carray* ids, int* check_self);
288 int mrmailbox_get_archived_count__ (mrmailbox_t*);
289 int mrmailbox_reset_tables (mrmailbox_t*, int bits); /* reset tables but leaves server configuration, 1=jobs, 2=e2ee, 8=rest but server config */
290 size_t mrmailbox_get_real_contact_cnt__ (mrmailbox_t*);
291 uint32_t mrmailbox_add_or_lookup_contact__ (mrmailbox_t*, const char* display_name /*can be NULL*/, const char* addr_spec, int origin, int* sth_modified);
292 int mrmailbox_get_contact_origin__ (mrmailbox_t*, uint32_t id, int* ret_blocked);
293 int mrmailbox_is_contact_blocked__ (mrmailbox_t*, uint32_t id);
294 int mrmailbox_real_contact_exists__ (mrmailbox_t*, uint32_t id);
295 int mrmailbox_contact_addr_equals__ (mrmailbox_t*, uint32_t contact_id, const char* other_addr);
296 void mrmailbox_scaleup_contact_origin__ (mrmailbox_t*, uint32_t contact_id, int origin);
297 
298 
299 /* library private: end-to-end-encryption */
300 #define MR_E2EE_DEFAULT_ENABLED 1
301 #define MR_MDNS_DEFAULT_ENABLED 1
302 
303 typedef struct mrmailbox_e2ee_helper_t {
304  int m_encryption_successfull;
305  void* m_cdata_to_free;
306 } mrmailbox_e2ee_helper_t;
307 
308 void mrmailbox_e2ee_encrypt (mrmailbox_t*, const clist* recipients_addr, int e2ee_guaranteed, int encrypt_to_self, struct mailmime* in_out_message, mrmailbox_e2ee_helper_t*);
309 int mrmailbox_e2ee_decrypt (mrmailbox_t*, struct mailmime* in_out_message, int* ret_validation_errors); /* returns 1 if sth. was decrypted, 0 in other cases */
310 void mrmailbox_e2ee_thanks (mrmailbox_e2ee_helper_t*); /* frees data referenced by "mailmime" but not freed by mailmime_free(). After calling mre2ee_unhelp(), in_out_message cannot be used any longer! */
311 int mrmailbox_ensure_secret_key_exists (mrmailbox_t*); /* makes sure, the private key exists, needed only for exporting keys and the case no message was sent before */
312 
313 
314 /*******************************************************************************
315  * Events
316  ******************************************************************************/
317 
318 
319 /* The following events may be passed to the callback given to mrmailbox_new() */
320 
321 
322 /* Information, should not be reported, can be logged,
323 data1=0, data2=info string */
324 #define MR_EVENT_INFO 100
325 
326 
327 /* Warning, should not be reported, should be logged
328 data1=0, data2=warning string */
329 #define MR_EVENT_WARNING 300
330 
331 
332 /* Error, must be reported to the user by a non-disturbing bubble or so.
333 data1=error code MR_ERR_*, see below, data2=error string */
334 #define MR_EVENT_ERROR 400
335 
336 
337 /* one or more messages changed for some reasons in the database - added or
338 removed. For added messages: data1=chat_id, data2=msg_id */
339 #define MR_EVENT_MSGS_CHANGED 2000
340 
341 
342 /* For fresh messages from the INBOX, MR_EVENT_INCOMING_MSG is send;
343 data1=chat_id, data2=msg_id */
344 #define MR_EVENT_INCOMING_MSG 2005
345 
346 
347 /* a single message is send successfully (state changed from PENDING/SENDING to
348 DELIVERED); data1=chat_id, data2=msg_id */
349 #define MR_EVENT_MSG_DELIVERED 2010
350 
351 
352 /* a single message is read by the receiver (state changed from DELIVERED to
353 READ); data1=chat_id, data2=msg_id */
354 #define MR_EVENT_MSG_READ 2015
355 
356 
357 /* group name/image changed or members added/removed */
358 #define MR_EVENT_CHAT_MODIFIED 2020
359 
360 
361 /* contact(s) created, renamed, blocked or deleted */
362 #define MR_EVENT_CONTACTS_CHANGED 2030
363 
364 
365 /* connection state changed,
366 data1=0:failed-not-connected, 1:configured-and-connected */
367 #define MR_EVENT_CONFIGURE_ENDED 2040
368 
369 
370 /* data1=percent */
371 #define MR_EVENT_CONFIGURE_PROGRESS 2041
372 
373 
374 /* mrmailbox_imex() done:
375 data1=0:failed, 1=success */
376 #define MR_EVENT_IMEX_ENDED 2050
377 
378 
379 /* data1=permille */
380 #define MR_EVENT_IMEX_PROGRESS 2051
381 
382 
383 /* file written, event may be needed to make the file public to some system
384 services. data1=file name, data2=mime type */
385 #define MR_EVENT_IMEX_FILE_WRITTEN 2052
386 
387 
388 /* The following events are functions that should be provided by the frontends */
389 
390 
391 /* check, if the system is online currently
392 ret=0: not online, ret=1: online */
393 #define MR_EVENT_IS_ONLINE 2080
394 
395 
396 /* get a string from the frontend, data1=MR_STR_*, ret=string which will be
397 free()'d by the backend */
398 #define MR_EVENT_GET_STRING 2091
399 
400 
401 /* synchronous http/https(!) call, data1=url, ret=content which will be
402 free()'d by the backend, 0 on errors */
403 #define MR_EVENT_GET_QUANTITY_STRING 2092
404 
405 
406 /* synchronous http/https(!) call, data1=url, ret=content which will be free()'d
407 by the backend, 0 on errors */
408 #define MR_EVENT_HTTP_GET 2100
409 
410 /* acquire wakeLock (data1=1) or release it (data1=0), the backend does not make
411 nested or unsynchronized calls */
412 #define MR_EVENT_WAKE_LOCK 2110
413 
414 
415 /* Error codes */
416 #define MR_ERR_SELF_NOT_IN_GROUP 1
417 #define MR_ERR_NONETWORK 2
418 
419 
420 /*******************************************************************************
421  * Import/export and Tools
422  ******************************************************************************/
423 
424 
425 #define MR_IMEX_CANCEL 0
426 #define MR_IMEX_EXPORT_SELF_KEYS 1
427 #define MR_IMEX_IMPORT_SELF_KEYS 2
428 #define MR_IMEX_EXPORT_BACKUP 11
429 #define MR_IMEX_IMPORT_BACKUP 12
430 #define MR_IMEX_EXPORT_SETUP_MESSAGE 20
431 #define MR_BAK_PREFIX "delta-chat"
432 #define MR_BAK_SUFFIX "bak"
433 void mrmailbox_imex (mrmailbox_t*, int what, const char* param1, const char* setup_code);
434 char* mrmailbox_imex_has_backup (mrmailbox_t*, const char* dir);
435 
436 
437 
438 int mrmailbox_check_password (mrmailbox_t*, const char* pw);
439 
440 
441 
442 char* mrmailbox_create_setup_code (mrmailbox_t*);
443 
444 
445 
446 int mrmailbox_poke_spec (mrmailbox_t*, const char* spec);
447 
448 
449 /* The library tries itself to stay alive. For this purpose there is an additional
450 "heartbeat" thread that checks if the IDLE-thread is up and working. This check is done about every minute.
451 However, depending on the operating system, this thread may be delayed or stopped, if this is the case you can
452 force additional checks manually by just calling mrmailbox_heartbeat() about every minute.
453 If in doubt, call this function too often, not too less :-) */
454 void mrmailbox_heartbeat (mrmailbox_t*);
455 
456 
457 /* carray tools, already defined are things as
458 unsigned unt carray_count() */
459 uint32_t carray_get_uint32 (carray*, unsigned int index);
460 
461 
462 /* deprecated functions */
463 mrchat_t* mrchatlist_get_chat_by_index (mrchatlist_t*, size_t index); /* deprecated - use mrchatlist_get_chat_id_by_index() */
464 mrmsg_t* mrchatlist_get_msg_by_index (mrchatlist_t*, size_t index); /* deprecated - use mrchatlist_get_msg_id_by_index() */
465 int mrchat_set_draft (mrchat_t*, const char* msg); /* deprecated - use mrmailbox_set_draft() instead */
466 
467 
468 /* library-internal */
469 void mrmailbox_unarchive_chat__ (mrmailbox_t*, uint32_t chat_id);
470 size_t mrmailbox_get_chat_cnt__ (mrmailbox_t*);
471 uint32_t mrmailbox_create_or_lookup_nchat_by_contact_id__(mrmailbox_t*, uint32_t contact_id);
472 uint32_t mrmailbox_lookup_real_nchat_by_contact_id__(mrmailbox_t*, uint32_t contact_id);
473 int mrmailbox_get_total_msg_count__ (mrmailbox_t*, uint32_t chat_id);
474 int mrmailbox_get_fresh_msg_count__ (mrmailbox_t*, uint32_t chat_id);
475 uint32_t mrmailbox_get_last_deaddrop_fresh_msg__(mrmailbox_t*);
476 void mrmailbox_send_msg_to_smtp (mrmailbox_t*, mrjob_t*);
477 void mrmailbox_send_msg_to_imap (mrmailbox_t*, mrjob_t*);
478 int mrmailbox_add_contact_to_chat__ (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
479 int mrmailbox_is_contact_in_chat__ (mrmailbox_t*, uint32_t chat_id, uint32_t contact_id);
480 int mrmailbox_get_chat_contact_count__ (mrmailbox_t*, uint32_t chat_id);
481 int mrmailbox_group_explicitly_left__ (mrmailbox_t*, const char* grpid);
482 void mrmailbox_set_group_explicitly_left__ (mrmailbox_t*, const char* grpid);
483 
484 size_t mrmailbox_get_real_msg_cnt__ (mrmailbox_t*); /* the number of messages assigned to real chat (!=deaddrop, !=trash) */
485 size_t mrmailbox_get_deaddrop_msg_cnt__ (mrmailbox_t*);
486 int mrmailbox_rfc724_mid_cnt__ (mrmailbox_t*, const char* rfc724_mid);
487 int mrmailbox_rfc724_mid_exists__ (mrmailbox_t*, const char* rfc724_mid, char** ret_server_folder, uint32_t* ret_server_uid);
488 void mrmailbox_update_server_uid__ (mrmailbox_t*, const char* rfc724_mid, const char* server_folder, uint32_t server_uid);
489 void mrmailbox_update_msg_chat_id__ (mrmailbox_t*, uint32_t msg_id, uint32_t chat_id);
490 void mrmailbox_update_msg_state__ (mrmailbox_t*, uint32_t msg_id, int state);
491 void mrmailbox_delete_msg_on_imap (mrmailbox_t* mailbox, mrjob_t* job);
492 int mrmailbox_mdn_from_ext__ (mrmailbox_t*, uint32_t from_id, const char* rfc724_mid, uint32_t* ret_chat_id, uint32_t* ret_msg_id); /* returns 1 if an event should be send */
493 void mrmailbox_send_mdn (mrmailbox_t*, mrjob_t* job);
494 void mrmailbox_markseen_msg_on_imap (mrmailbox_t* mailbox, mrjob_t* job);
495 void mrmailbox_markseen_mdn_on_imap (mrmailbox_t* mailbox, mrjob_t* job);
496 
497 
498 #ifdef __cplusplus
499 } /* /extern "C" */
500 #endif
501 #endif /* __MRMAILBOX_H__ */
int mrmailbox_add_address_book(mrmailbox_t *mailbox, const char *adr_book)
Add a number of contacts.
Definition: mrmailbox.c:4021
+
uint32_t mrmailbox_create_group_chat(mrmailbox_t *mailbox, const char *chat_name)
Create a new group chat.
Definition: mrmailbox.c:3333
+
uint32_t mrmailbox_create_contact(mrmailbox_t *mailbox, const char *name, const char *addr)
Add a single contact.
Definition: mrmailbox.c:3985
+
uint32_t mrmailbox_send_text_msg(mrmailbox_t *mailbox, uint32_t chat_id, const char *text_to_send)
Send a simple text message to the given chat.
Definition: mrmailbox.c:3101
+
void mrmailbox_close(mrmailbox_t *mailbox)
Close mailbox database.
Definition: mrmailbox.c:1075
+
mrmailbox_t * mrmailbox_new(mrmailboxcb_t cb, void *userdata, const char *os_name)
Create a new mailbox object.
Definition: mrmailbox.c:897
+
int mrmailbox_is_contact_in_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Check if a given contact ID is a member of a group chat.
Definition: mrmailbox.c:3565
+
mrchat_t * mrmailbox_get_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Get a chat object of type mrchat_t by a chat_id.
Definition: mrmailbox.c:1709
+
int mrmailbox_remove_contact_from_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Remove a member from a group.
Definition: mrmailbox.c:3678
+
int mrmailbox_open(mrmailbox_t *mailbox, const char *dbfile, const char *blobdir)
Open mailbox database.
Definition: mrmailbox.c:1010
+
An object representing a single contact in memory.
Definition: mrcontact.h:38
+
int mrmailbox_delete_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Delete a contact.
Definition: mrmailbox.c:4509
+
An object representing a single chatlist in memory.
Definition: mrchatlist.h:42
+
carray * mrmailbox_get_chat_msgs(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t flags, uint32_t marker1before)
Get all message IDs belonging to a chat.
Definition: mrmailbox.c:2120
+
void mrmailbox_star_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt, int star)
Star/unstar messages by setting the last parameter to 0 (unstar) or 1(star).
Definition: mrmailbox.c:4954
+
carray * mrmailbox_search_msgs(mrmailbox_t *mailbox, uint32_t chat_id, const char *query)
Search messages containing the given query string.
Definition: mrmailbox.c:2224
+
int mrmailbox_set_config(mrmailbox_t *ths, const char *key, const char *value)
Configure the mailbox.
Definition: mrmailbox.c:1176
+
char * m_dbfile
the database file in file.
Definition: mrmailbox.h:144
+
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
void mrmailbox_delete_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt)
Delete a list of messages.
Definition: mrmailbox.c:5096
+
int mrmailbox_is_open(const mrmailbox_t *mailbox)
Check if a given mailbox database is open.
Definition: mrmailbox.c:1109
+
void mrmailbox_archive_chat(mrmailbox_t *mailbox, uint32_t chat_id, int archive)
Archive or unarchive a chat.
Definition: mrmailbox.c:2652
+
char * mrmailbox_get_info(mrmailbox_t *mailbox)
Get information about the mailbox.
Definition: mrmailbox.c:1292
+
int mrmailbox_add_contact_to_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Add a member to a group.
Definition: mrmailbox.c:3595
+
uint32_t mrmailbox_get_next_media(mrmailbox_t *mailbox, uint32_t curr_msg_id, int dir)
Returns all message IDs of the given types in a chat.
Definition: mrmailbox.c:1927
+
char * mrmailbox_get_version_str(void)
Find out the version of the Delta Chat core library.
Definition: mrmailbox.c:1490
+
void mrmailbox_disconnect(mrmailbox_t *mailbox)
Disonnect the mailbox from the server.
Definition: mrmailbox.c:1605
+
char * m_blobdir
full path of the blob directory in use.
Definition: mrmailbox.h:145
+
carray * mrmailbox_get_fresh_msgs(mrmailbox_t *mailbox)
Returns the message IDs of all fresh messages of any chat.
Definition: mrmailbox.c:2052
+
uint32_t mrmailbox_send_msg(mrmailbox_t *mailbox, uint32_t chat_id, mrmsg_t *msg)
save message in database and send it, the given message object is not unref&#39;d by the function but som...
Definition: mrmailbox.c:3142
+
void mrmailbox_connect(mrmailbox_t *mailbox)
Connect to the mailbox using the configured settings.
Definition: mrmailbox.c:1578
+
int mrmailbox_get_total_msg_count(mrmailbox_t *mailbox, uint32_t chat_id)
Get the total number of messages in a chat.
Definition: mrmailbox.c:2588
+
carray * mrmailbox_get_chat_contacts(mrmailbox_t *mailbox, uint32_t chat_id)
Get contact IDs belonging to a chat.
Definition: mrmailbox.c:2003
+
void mrmailbox_unref(mrmailbox_t *mailbox)
Free a mailbox object.
Definition: mrmailbox.c:954
+
void mrmailbox_block_contact(mrmailbox_t *mailbox, uint32_t contact_id, int new_blocking)
Block or unblock a contact.
Definition: mrmailbox.c:4290
+
uintptr_t(* mrmailboxcb_t)(mrmailbox_t *, int event, uintptr_t data1, uintptr_t data2)
Callback function that should be given to mrmailbox_new().
Definition: mrmailbox.h:131
+
mrchatlist_t * mrmailbox_get_chatlist(mrmailbox_t *mailbox, int listflags, const char *query)
Get a list of chats.
Definition: mrmailbox.c:1663
+
An object representing a single message in memory.
Definition: mrmsg.h:40
+
void mrmailbox_configure_and_connect(mrmailbox_t *mailbox)
Configure and connect a mailbox.
Definition: mrmailbox_configure.c:665
+
uint32_t mrmailbox_get_chat_id_by_contact_id(mrmailbox_t *mailbox, uint32_t contact_id)
Check, if there is a normal chat with a given contact.
Definition: mrmailbox.c:1789
+
int mrmailbox_get_fresh_msg_count(mrmailbox_t *mailbox, uint32_t chat_id)
Get the number of fresh messages in a chat.
Definition: mrmailbox.c:2616
+
void mrmailbox_delete_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Delete a chat:
Definition: mrmailbox.c:2773
+
void mrmailbox_set_draft(mrmailbox_t *mailbox, uint32_t chat_id, const char *msg)
Save a draft for a chat.
Definition: mrmailbox.c:2371
+
int mrmailbox_get_blocked_count(mrmailbox_t *mailbox)
Get the number of blocked contacts.
Definition: mrmailbox.c:4174
+
int mrmailbox_set_chat_image(mrmailbox_t *mailbox, uint32_t chat_id, const char *new_image)
Set group image.
Definition: mrmailbox.c:3476
+
carray * mrmailbox_get_known_contacts(mrmailbox_t *mailbox, const char *query)
Returns known and unblocked contacts.
Definition: mrmailbox.c:4076
+
char * mrmailbox_get_blobdir(mrmailbox_t *mailbox)
Get the blob directory.
Definition: mrmailbox.c:1276
+
mrmsg_t * mrmailbox_get_msg(mrmailbox_t *mailbox, uint32_t msg_id)
Get a single message object of the type mrmsg_t.
Definition: mrmailbox.c:4701
+
void * m_userdata
the same pointer as given to mrmailbox_new(), may be used by the caller for any purpose ...
Definition: mrmailbox.h:143
+
int mrmailbox_is_configured(mrmailbox_t *mailbox)
Check if the mailbox is already configured.
Definition: mrmailbox_configure.c:735
+
carray * mrmailbox_get_chat_media(mrmailbox_t *mailbox, uint32_t chat_id, int msg_type, int or_msg_type)
Returns all message IDs of the given types in a chat.
Definition: mrmailbox.c:1896
+
uint32_t mrmailbox_create_chat_by_contact_id(mrmailbox_t *mailbox, uint32_t contact_id)
Create a normal chat with a single user.
Definition: mrmailbox.c:1816
+
void mrmailbox_forward_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt, uint32_t chat_id)
Forward a list of messages to another chat.
Definition: mrmailbox.c:4868
+
void mrmailbox_markseen_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt)
Mark a message as seen, updates the IMAP state and sends MDNs.
Definition: mrmailbox.c:5237
+
void mrmailbox_marknoticed_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Mark all message in a chat as noticed.
Definition: mrmailbox.c:1755
+
char * mrmailbox_imex_has_backup(mrmailbox_t *mailbox, const char *dir_name)
Check if there is a backup file.
Definition: mrmailbox_imex.c:849
+
void mrmailbox_configure_cancel(mrmailbox_t *mailbox)
Cancel an configuration started by mrmailbox_configure_and_connect().
Definition: mrmailbox_configure.c:709
+
carray * mrmailbox_get_blocked_contacts(mrmailbox_t *mailbox)
Get blocked contacts.
Definition: mrmailbox.c:4140
+
mrcontact_t * mrmailbox_get_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Get a single contact object.
Definition: mrmailbox.c:4216
+
int mrmailbox_check_password(mrmailbox_t *mailbox, const char *test_pw)
Check if the user is authorized by the given password in some way.
Definition: mrmailbox_imex.c:1161
+
void mrmailbox_marknoticed_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Mark all messages send by the given contact as noticed.
Definition: mrmailbox.c:4264
+
int mrmailbox_set_config_int(mrmailbox_t *ths, const char *key, int32_t value)
Configure the mailbox.
Definition: mrmailbox.c:1229
+
int32_t mrmailbox_get_config_int(mrmailbox_t *ths, const char *key, int32_t def)
Get a configuration option.
Definition: mrmailbox.c:1251
+
int mrmailbox_set_chat_name(mrmailbox_t *mailbox, uint32_t chat_id, const char *new_name)
Set group name.
Definition: mrmailbox.c:3399
+
char * mrmailbox_create_setup_code(mrmailbox_t *mailbox)
Create random setup code.
Definition: mrmailbox_imex.c:1218
+
char * mrmailbox_get_contact_encrinfo(mrmailbox_t *mailbox, uint32_t contact_id)
Get encryption info.
Definition: mrmailbox.c:4381
+
void mrmailbox_imex(mrmailbox_t *mailbox, int what, const char *param1, const char *setup_code)
Import/export things.
Definition: mrmailbox_imex.c:1108
+
char * mrmailbox_get_config(mrmailbox_t *ths, const char *key, const char *def)
Get a configuration option.
Definition: mrmailbox.c:1207
+
char * mrmailbox_get_msg_info(mrmailbox_t *mailbox, uint32_t msg_id)
Get an informational text for a single message.
Definition: mrmailbox.c:4743
+
An object representing a single chat in memory.
Definition: mrchat.h:39
diff --git a/docs/user/html/mrmailbox__internal_8h_source.html b/docs/html/mrmailbox__internal_8h_source.html similarity index 99% rename from docs/user/html/mrmailbox__internal_8h_source.html rename to docs/html/mrmailbox__internal_8h_source.html index 0da9bebc..30c607a1 100644 --- a/docs/user/html/mrmailbox__internal_8h_source.html +++ b/docs/html/mrmailbox__internal_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMAILBOX_INTERNAL_H__
24 #define __MRMAILBOX_INTERNAL_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /* Includes that are used frequently. This file may also be used to create predefined headers. */
31 #include "mrmailbox.h"
32 #include <stdlib.h>
33 #include <string.h>
34 #include "mrsqlite3.h"
35 #include "mrtools.h"
36 #include "mrstock.h"
37 
38 
39 #ifdef __cplusplus
40 } /* /extern "C" */
41 #endif
42 #endif /* __MRMAILBOX_INTERNAL_H__ */
43 
diff --git a/docs/user/html/mrmimefactory_8h_source.html b/docs/html/mrmimefactory_8h_source.html similarity index 97% rename from docs/user/html/mrmimefactory_8h_source.html rename to docs/html/mrmimefactory_8h_source.html index d8984856..3bc2e30d 100644 --- a/docs/user/html/mrmimefactory_8h_source.html +++ b/docs/html/mrmimefactory_8h_source.html @@ -88,13 +88,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrmimefactory.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMIMEFACTORY_H__
24 #define __MRMIMEFACTORY_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 
31 
32 typedef struct mrmsg_t mrmsg_t;
33 typedef struct mrchat_t mrchat_t;
34 typedef struct mrmailbox_t mrmailbox_t;
35 
36 
37 #define MR_SYSTEM_GROUPNAME_CHANGED 2
38 #define MR_SYSTEM_GROUPIMAGE_CHANGED 3
39 #define MR_SYSTEM_MEMBER_ADDED_TO_GROUP 4
40 #define MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP 5
41 
42 
43 typedef enum {
44  MR_MF_NOTHING_LOADED = 0,
45  MR_MF_MSG_LOADED,
46  MR_MF_MDN_LOADED
47 } mrmimefactory_loaded_t;
48 
49 
53 typedef struct mrmimefactory_t {
54 
57  /* in: parameters, set eg. by mrmimefactory_load_msg() */
58  char* m_from_addr;
59  char* m_from_displayname;
60  char* m_selfstatus;
61  clist* m_recipients_names;
62  clist* m_recipients_addr;
63  time_t m_timestamp;
64  char* m_rfc724_mid;
65 
66  /* what is loaded? */
67  mrmimefactory_loaded_t m_loaded;
68 
69  mrmsg_t* m_msg;
70  mrchat_t* m_chat;
71  int m_increation;
72  char* m_predecessor;
73  char* m_references;
74  int m_req_mdn;
75 
76  /* out: after a successfull mrmimefactory_create_mime(), here's the data */
77  MMAPString* m_out;
78  int m_out_encrypted;
79 
80  /* private */
81  mrmailbox_t* m_mailbox;
82 
83 } mrmimefactory_t;
84 
85 
86 void mrmimefactory_init (mrmimefactory_t*, mrmailbox_t*);
87 void mrmimefactory_empty (mrmimefactory_t*);
88 int mrmimefactory_load_msg (mrmimefactory_t*, uint32_t msg_id);
89 int mrmimefactory_load_mdn (mrmimefactory_t*, uint32_t msg_id);
90 int mrmimefactory_render (mrmimefactory_t*, int encrypt_to_self);
91 
92 
93 #ifdef __cplusplus
94 } /* /extern "C" */
95 #endif
96 #endif /* __MRMIMEFACTORY_H__ */
97 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
An object representing a single chat in memory.
Definition: mrchat.h:39
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMIMEFACTORY_H__
24 #define __MRMIMEFACTORY_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 
31 
32 typedef struct mrmsg_t mrmsg_t;
33 typedef struct mrchat_t mrchat_t;
34 typedef struct mrmailbox_t mrmailbox_t;
35 
36 
37 #define MR_SYSTEM_GROUPNAME_CHANGED 2
38 #define MR_SYSTEM_GROUPIMAGE_CHANGED 3
39 #define MR_SYSTEM_MEMBER_ADDED_TO_GROUP 4
40 #define MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP 5
41 
42 
43 typedef enum {
44  MR_MF_NOTHING_LOADED = 0,
45  MR_MF_MSG_LOADED,
46  MR_MF_MDN_LOADED
47 } mrmimefactory_loaded_t;
48 
49 
53 typedef struct mrmimefactory_t {
54 
57  /* in: parameters, set eg. by mrmimefactory_load_msg() */
58  char* m_from_addr;
59  char* m_from_displayname;
60  char* m_selfstatus;
61  clist* m_recipients_names;
62  clist* m_recipients_addr;
63  time_t m_timestamp;
64  char* m_rfc724_mid;
65 
66  /* what is loaded? */
67  mrmimefactory_loaded_t m_loaded;
68 
69  mrmsg_t* m_msg;
70  mrchat_t* m_chat;
71  int m_increation;
72  char* m_predecessor;
73  char* m_references;
74  int m_req_mdn;
75 
76  /* out: after a successfull mrmimefactory_create_mime(), here's the data */
77  MMAPString* m_out;
78  int m_out_encrypted;
79 
80  /* private */
81  mrmailbox_t* m_mailbox;
82 
83 } mrmimefactory_t;
84 
85 
86 void mrmimefactory_init (mrmimefactory_t*, mrmailbox_t*);
87 void mrmimefactory_empty (mrmimefactory_t*);
88 int mrmimefactory_load_msg (mrmimefactory_t*, uint32_t msg_id);
89 int mrmimefactory_load_mdn (mrmimefactory_t*, uint32_t msg_id);
90 int mrmimefactory_render (mrmimefactory_t*, int encrypt_to_self);
91 
92 
93 #ifdef __cplusplus
94 } /* /extern "C" */
95 #endif
96 #endif /* __MRMIMEFACTORY_H__ */
97 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
An object representing a single message in memory.
Definition: mrmsg.h:40
+
An object representing a single chat in memory.
Definition: mrchat.h:39
diff --git a/docs/user/html/mrmimeparser_8h_source.html b/docs/html/mrmimeparser_8h_source.html similarity index 98% rename from docs/user/html/mrmimeparser_8h_source.html rename to docs/html/mrmimeparser_8h_source.html index 04cb8885..1ece85c4 100644 --- a/docs/user/html/mrmimeparser_8h_source.html +++ b/docs/html/mrmimeparser_8h_source.html @@ -88,12 +88,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrmimeparser.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Parse MIME body; this is the text part of an IMF, see https://tools.ietf.org/html/rfc5322
24 mrmimeparser_t has no deep dependencies to mrmailbox_t or to the database
25 (mrmailbox_t is used for logging only). */
26 
27 
28 #ifndef __MRMIMEPARSER_H__
29 #define __MRMIMEPARSER_H__
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 
38 typedef struct mrmimepart_t
39 {
41  int m_type; /*one of MR_MSG_* */
42  int m_is_meta; /*meta parts contain eg. profile or group images and are only present if there is at least one "normal" part*/
43  char* m_msg;
44  char* m_msg_raw;
45  int m_bytes;
46  mrparam_t* m_param;
47 } mrmimepart_t;
48 
49 
53 typedef struct mrmimeparser_t
54 {
57  /* data, read-only, must not be free()'d (it is free()'d when the MrMimeParser object gets destructed) */
58  carray* m_parts; /*array of mrmimepart_t objects*/
59  struct mailmime* m_mimeroot;
60  struct mailimf_fields* m_header; /* a pointer somewhere to the MIME data, must not be freed */
61  char* m_subject;
62  int m_is_send_by_messenger;
63  int m_decrypted_and_validated;
64  int m_decrypted_with_validation_errors;
65  int m_decrypting_failed; /* set, if there are multipart/encrypted parts left after decryption */
66  const char* m_blobdir;
67 
68  int m_is_forwarded;
69 
70  mrmailbox_t* m_mailbox;
71 
72  carray* m_reports; /* array of mailmime objects */
73 
74  int m_is_system_message;
75 
76 } mrmimeparser_t;
77 
78 
79 mrmimeparser_t* mrmimeparser_new (const char* blobdir, mrmailbox_t*);
80 void mrmimeparser_unref (mrmimeparser_t*);
81 void mrmimeparser_empty (mrmimeparser_t*);
82 
83 /* The data returned from Parse() must not be freed (it is free()'d when the MrMimeParser object gets destructed)
84 Unless memory-allocation-errors occur, Parse() returns at least one empty part.
85 (this is because we want to add even these message to our database to avoid reading them several times.
86 of course, these empty messages are not added to any chat) */
87 void mrmimeparser_parse (mrmimeparser_t*, const char* body_not_terminated, size_t body_bytes);
88 
89 /* mrmimeparser_get_last_nonmeta() gets the _last_ part _not_ flagged with m_is_meta. */
90 mrmimepart_t* mrmimeparser_get_last_nonmeta (mrmimeparser_t*);
91 #define mrmimeparser_has_nonmeta(a) (mrmimeparser_get_last_nonmeta((a))!=NULL)
92 
93 /* mrmimeparser_is_mailinglist_message() just checks if there is a `List-ID`-header. */
94 int mrmimeparser_is_mailinglist_message (mrmimeparser_t*);
95 
96 /* low-level-tools for working with mailmime structures directly */
97 char* mr_find_first_addr (const struct mailimf_mailbox_list*); /*the result must be freed*/
98 char* mr_normalize_addr (const char*); /*the result must be freed*/
99 struct mailimf_fields* mr_find_mailimf_fields(struct mailmime*); /*the result is a pointer to mime, must not be freed*/
100 struct mailimf_field* mr_find_mailimf_field (struct mailimf_fields*, int wanted_fld_type); /*the result is a pointer to mime, must not be freed*/
101 struct mailimf_optional_field* mr_find_mailimf_field2(struct mailimf_fields*, const char* wanted_fld_name);
102 struct mailmime_parameter* mr_find_ct_parameter (struct mailmime*, const char* name);
103 int mr_mime_transfer_decode(struct mailmime*, const char** ret_decoded_data, size_t* ret_decoded_data_bytes, char** ret_to_mmap_string_unref);
104 
105 
106 #ifdef MR_USE_MIME_DEBUG
107 void mr_print_mime(struct mailmime * mime);
108 #endif
109 
110 
111 #ifdef __cplusplus
112 } /* /extern "C" */
113 #endif
114 #endif /* __MRMIMEPARSER_H__ */
115 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Parse MIME body; this is the text part of an IMF, see https://tools.ietf.org/html/rfc5322
24 mrmimeparser_t has no deep dependencies to mrmailbox_t or to the database
25 (mrmailbox_t is used for logging only). */
26 
27 
28 #ifndef __MRMIMEPARSER_H__
29 #define __MRMIMEPARSER_H__
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 
38 typedef struct mrmimepart_t
39 {
41  int m_type; /*one of MR_MSG_* */
42  int m_is_meta; /*meta parts contain eg. profile or group images and are only present if there is at least one "normal" part*/
43  char* m_msg;
44  char* m_msg_raw;
45  int m_bytes;
46  mrparam_t* m_param;
47 } mrmimepart_t;
48 
49 
53 typedef struct mrmimeparser_t
54 {
57  /* data, read-only, must not be free()'d (it is free()'d when the MrMimeParser object gets destructed) */
58  carray* m_parts; /*array of mrmimepart_t objects*/
59  struct mailmime* m_mimeroot;
60  struct mailimf_fields* m_header; /* a pointer somewhere to the MIME data, must not be freed */
61  char* m_subject;
62  int m_is_send_by_messenger;
63  int m_decrypted_and_validated;
64  int m_decrypted_with_validation_errors;
65  int m_decrypting_failed; /* set, if there are multipart/encrypted parts left after decryption */
66  const char* m_blobdir;
67 
68  int m_is_forwarded;
69 
70  mrmailbox_t* m_mailbox;
71 
72  carray* m_reports; /* array of mailmime objects */
73 
74  int m_is_system_message;
75 
76 } mrmimeparser_t;
77 
78 
79 mrmimeparser_t* mrmimeparser_new (const char* blobdir, mrmailbox_t*);
80 void mrmimeparser_unref (mrmimeparser_t*);
81 void mrmimeparser_empty (mrmimeparser_t*);
82 
83 /* The data returned from Parse() must not be freed (it is free()'d when the MrMimeParser object gets destructed)
84 Unless memory-allocation-errors occur, Parse() returns at least one empty part.
85 (this is because we want to add even these message to our database to avoid reading them several times.
86 of course, these empty messages are not added to any chat) */
87 void mrmimeparser_parse (mrmimeparser_t*, const char* body_not_terminated, size_t body_bytes);
88 
89 /* mrmimeparser_get_last_nonmeta() gets the _last_ part _not_ flagged with m_is_meta. */
90 mrmimepart_t* mrmimeparser_get_last_nonmeta (mrmimeparser_t*);
91 #define mrmimeparser_has_nonmeta(a) (mrmimeparser_get_last_nonmeta((a))!=NULL)
92 
93 /* mrmimeparser_is_mailinglist_message() just checks if there is a `List-ID`-header. */
94 int mrmimeparser_is_mailinglist_message (mrmimeparser_t*);
95 
96 /* low-level-tools for working with mailmime structures directly */
97 char* mr_find_first_addr (const struct mailimf_mailbox_list*); /*the result must be freed*/
98 char* mr_normalize_addr (const char*); /*the result must be freed*/
99 struct mailimf_fields* mr_find_mailimf_fields(struct mailmime*); /*the result is a pointer to mime, must not be freed*/
100 struct mailimf_field* mr_find_mailimf_field (struct mailimf_fields*, int wanted_fld_type); /*the result is a pointer to mime, must not be freed*/
101 struct mailimf_optional_field* mr_find_mailimf_field2(struct mailimf_fields*, const char* wanted_fld_name);
102 struct mailmime_parameter* mr_find_ct_parameter (struct mailmime*, const char* name);
103 int mr_mime_transfer_decode(struct mailmime*, const char** ret_decoded_data, size_t* ret_decoded_data_bytes, char** ret_to_mmap_string_unref);
104 
105 
106 #ifdef MR_USE_MIME_DEBUG
107 void mr_print_mime(struct mailmime * mime);
108 #endif
109 
110 
111 #ifdef __cplusplus
112 } /* /extern "C" */
113 #endif
114 #endif /* __MRMIMEPARSER_H__ */
115 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
An object for handling key=value parameter lists.
Definition: mrparam.h:36
diff --git a/docs/user/html/mrmsg_8h_source.html b/docs/html/mrmsg_8h_source.html similarity index 94% rename from docs/user/html/mrmsg_8h_source.html rename to docs/html/mrmsg_8h_source.html index 46a74aad..f275accf 100644 --- a/docs/user/html/mrmsg_8h_source.html +++ b/docs/html/mrmsg_8h_source.html @@ -88,29 +88,29 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrmsg.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMSG_H__
24 #define __MRMSG_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct mrparam_t mrparam_t;
32 typedef struct sqlite3_stmt sqlite3_stmt;
33 
34 
40 typedef struct mrmsg_t
41 {
42  #define MR_MSG_ID_MARKER1 1
43  #define MR_MSG_ID_DAYMARKER 9
44  #define MR_MSG_ID_LAST_SPECIAL 9
45  uint32_t m_id;
47  uint32_t m_from_id;
48  uint32_t m_to_id;
49  uint32_t m_chat_id;
50  time_t m_timestamp;
52  #define MR_MSG_UNDEFINED 0
53  #define MR_MSG_TEXT 10
54  #define MR_MSG_IMAGE 20
55  #define MR_MSG_GIF 21
56  #define MR_MSG_AUDIO 40
57  #define MR_MSG_VOICE 41
58  #define MR_MSG_VIDEO 50
59  #define MR_MSG_FILE 60
60  int m_type;
62  #define MR_STATE_UNDEFINED 0
63  #define MR_STATE_IN_FRESH 10
64  #define MR_STATE_IN_NOTICED 13
65  #define MR_STATE_IN_SEEN 16
66  #define MR_STATE_OUT_PENDING 20
67  #define MR_STATE_OUT_ERROR 24
68  #define MR_STATE_OUT_DELIVERED 26
69  #define MR_STATE_OUT_MDN_RCVD 28
70  int m_state;
72  char* m_text;
73  mrparam_t* m_param;
74  int m_starred;
75  int m_is_msgrmsg;
78  mrmailbox_t* m_mailbox;
79  char* m_rfc724_mid;
80  char* m_server_folder;
81  uint32_t m_server_uid;
82 } mrmsg_t;
83 
84 
85 mrmsg_t* mrmsg_new ();
86 void mrmsg_unref (mrmsg_t*);
87 void mrmsg_empty (mrmsg_t*);
89 char* mrmsg_get_summarytext (mrmsg_t*, int approx_characters);
91 char* mrmsg_get_fullpath (mrmsg_t*);
92 char* mrmsg_get_filename (mrmsg_t*);
94 int mrmsg_is_increation (mrmsg_t*);
96 void mrmsg_set_text (mrmsg_t*, const char* text);
97 
98 /* library-private */
99 #define MR_MSG_FIELDS " m.id,rfc724_mid,m.server_folder,m.server_uid,m.chat_id, m.from_id,m.to_id,m.timestamp, m.type,m.state,m.msgrmsg,m.txt, m.param,m.starred "
100 int mrmsg_set_from_stmt__ (mrmsg_t*, sqlite3_stmt* row, int row_offset); /* row order is MR_MSG_FIELDS */
101 int mrmsg_load_from_db__ (mrmsg_t*, mrmailbox_t*, uint32_t id);
102 int mrmsg_is_increation__ (const mrmsg_t*);
103 char* mrmsg_get_summarytext_by_raw (int type, const char* text, mrparam_t*, int approx_bytes); /* the returned value must be free()'d */
104 void mrmsg_save_param_to_disk__ (mrmsg_t*);
105 void mrmsg_guess_msgtype_from_suffix (const char* pathNfilename, int* ret_msgtype, char** ret_mime);
106 void mrmsg_get_authorNtitle_from_filename (const char* pathNfilename, char** ret_author, char** ret_title);
107 
108 #define MR_MSG_NEEDS_ATTACHMENT(a) ((a)==MR_MSG_IMAGE || (a)==MR_MSG_GIF || (a)==MR_MSG_AUDIO || (a)==MR_MSG_VOICE || (a)==MR_MSG_VIDEO || (a)==MR_MSG_FILE)
109 #define MR_MSG_MAKE_FILENAME_SEARCHABLE(a) ((a)==MR_MSG_AUDIO || (a)==MR_MSG_FILE || (a)==MR_MSG_VIDEO ) /* add filename.ext (without path) to m_text? this is needed for the fulltext search. The extension is useful to get all PDF, all MP3 etc. */
110 #define MR_MSG_MAKE_SUFFIX_SEARCHABLE(a) ((a)==MR_MSG_IMAGE || (a)==MR_MSG_GIF || (a)==MR_MSG_VOICE)
111 
112 #define APPROX_SUBJECT_CHARS 32 /* as we do not cut inside words, this results in about 32-42 characters.
113  Do not use too long subjects - we add a tag after the subject which gets truncated by the clients otherwise.
114  It should also be very clear, the subject is _not_ the whole message.
115  The value is also used for CC:-summaries */
116 
117 
118 #ifdef __cplusplus
119 } /* /extern "C" */
120 #endif
121 #endif /* __MRMSG_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
char * mrmsg_get_summarytext(mrmsg_t *msg, int approx_characters)
Get a message summary as a single line of text.
Definition: mrmsg.c:340
-
uint32_t m_chat_id
Chat ID the message belongs to.
Definition: mrmsg.h:49
-
mrmsg_t * mrmsg_new()
Create new message object.
Definition: mrmsg.c:41
-
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
-
mrpoortext_t * mrmsg_get_summary(mrmsg_t *msg, mrchat_t *chat)
Get a summary for a message.
Definition: mrmsg.c:263
-
mrpoortext_t * mrmsg_get_mediainfo(mrmsg_t *msg)
Get real author and title.
Definition: mrmsg.c:480
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
int mrmsg_show_padlock(mrmsg_t *msg)
Check if a padlock should be shown beside the message.
Definition: mrmsg.c:302
-
uint32_t m_to_id
Contact ID of the receiver, if appropriate.
Definition: mrmsg.h:48
-
uint32_t m_from_id
Contact ID of the sender.
Definition: mrmsg.h:47
-
void mrmsg_set_text(mrmsg_t *msg, const char *text)
Set the text of a message object.
Definition: mrmsg.c:125
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
-
void mrmsg_unref(mrmsg_t *msg)
Free an mrmsg_t object created eg.
Definition: mrmsg.c:66
-
uint32_t m_id
Message ID.
Definition: mrmsg.h:45
-
void mrmsg_save_param_to_disk(mrmsg_t *msg)
can be used to add some additional, persistent information to a messages record.
Definition: mrmsg.c:590
-
void mrmsg_empty(mrmsg_t *msg)
Empty a message object.
Definition: mrmsg.c:87
-
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
-
An object representing a single chat in memory.
Definition: mrchat.h:39
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRMSG_H__
24 #define __MRMSG_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef struct mrmailbox_t mrmailbox_t;
31 typedef struct mrparam_t mrparam_t;
32 typedef struct sqlite3_stmt sqlite3_stmt;
33 
34 
40 typedef struct mrmsg_t
41 {
42  #define MR_MSG_ID_MARKER1 1
43  #define MR_MSG_ID_DAYMARKER 9
44  #define MR_MSG_ID_LAST_SPECIAL 9
45  uint32_t m_id;
47  uint32_t m_from_id;
48  uint32_t m_to_id;
49  uint32_t m_chat_id;
50  time_t m_timestamp;
52  #define MR_MSG_UNDEFINED 0
53  #define MR_MSG_TEXT 10
54  #define MR_MSG_IMAGE 20
55  #define MR_MSG_GIF 21
56  #define MR_MSG_AUDIO 40
57  #define MR_MSG_VOICE 41
58  #define MR_MSG_VIDEO 50
59  #define MR_MSG_FILE 60
60  int m_type;
62  #define MR_STATE_UNDEFINED 0
63  #define MR_STATE_IN_FRESH 10
64  #define MR_STATE_IN_NOTICED 13
65  #define MR_STATE_IN_SEEN 16
66  #define MR_STATE_OUT_PENDING 20
67  #define MR_STATE_OUT_ERROR 24
68  #define MR_STATE_OUT_DELIVERED 26
69  #define MR_STATE_OUT_MDN_RCVD 28
70  int m_state;
72  char* m_text;
73  mrparam_t* m_param;
74  int m_starred;
75  int m_is_msgrmsg;
78  mrmailbox_t* m_mailbox;
79  char* m_rfc724_mid;
80  char* m_server_folder;
81  uint32_t m_server_uid;
82 } mrmsg_t;
83 
84 
85 mrmsg_t* mrmsg_new ();
86 void mrmsg_unref (mrmsg_t*);
87 void mrmsg_empty (mrmsg_t*);
89 char* mrmsg_get_summarytext (mrmsg_t*, int approx_characters);
91 char* mrmsg_get_fullpath (mrmsg_t*);
92 char* mrmsg_get_filename (mrmsg_t*);
94 int mrmsg_is_increation (mrmsg_t*);
96 void mrmsg_set_text (mrmsg_t*, const char* text);
97 
98 /* library-private */
99 #define MR_MSG_FIELDS " m.id,rfc724_mid,m.server_folder,m.server_uid,m.chat_id, m.from_id,m.to_id,m.timestamp, m.type,m.state,m.msgrmsg,m.txt, m.param,m.starred "
100 int mrmsg_set_from_stmt__ (mrmsg_t*, sqlite3_stmt* row, int row_offset); /* row order is MR_MSG_FIELDS */
101 int mrmsg_load_from_db__ (mrmsg_t*, mrmailbox_t*, uint32_t id);
102 int mrmsg_is_increation__ (const mrmsg_t*);
103 char* mrmsg_get_summarytext_by_raw (int type, const char* text, mrparam_t*, int approx_bytes); /* the returned value must be free()'d */
104 void mrmsg_save_param_to_disk__ (mrmsg_t*);
105 void mrmsg_guess_msgtype_from_suffix (const char* pathNfilename, int* ret_msgtype, char** ret_mime);
106 void mrmsg_get_authorNtitle_from_filename (const char* pathNfilename, char** ret_author, char** ret_title);
107 
108 #define MR_MSG_NEEDS_ATTACHMENT(a) ((a)==MR_MSG_IMAGE || (a)==MR_MSG_GIF || (a)==MR_MSG_AUDIO || (a)==MR_MSG_VOICE || (a)==MR_MSG_VIDEO || (a)==MR_MSG_FILE)
109 #define MR_MSG_MAKE_FILENAME_SEARCHABLE(a) ((a)==MR_MSG_AUDIO || (a)==MR_MSG_FILE || (a)==MR_MSG_VIDEO ) /* add filename.ext (without path) to m_text? this is needed for the fulltext search. The extension is useful to get all PDF, all MP3 etc. */
110 #define MR_MSG_MAKE_SUFFIX_SEARCHABLE(a) ((a)==MR_MSG_IMAGE || (a)==MR_MSG_GIF || (a)==MR_MSG_VOICE)
111 
112 #define APPROX_SUBJECT_CHARS 32 /* as we do not cut inside words, this results in about 32-42 characters.
113  Do not use too long subjects - we add a tag after the subject which gets truncated by the clients otherwise.
114  It should also be very clear, the subject is _not_ the whole message.
115  The value is also used for CC:-summaries */
116 
117 
118 #ifdef __cplusplus
119 } /* /extern "C" */
120 #endif
121 #endif /* __MRMSG_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
char * mrmsg_get_summarytext(mrmsg_t *msg, int approx_characters)
Get a message summary as a single line of text.
Definition: mrmsg.c:340
+
uint32_t m_chat_id
Chat ID the message belongs to.
Definition: mrmsg.h:49
+
mrmsg_t * mrmsg_new()
Create new message object.
Definition: mrmsg.c:41
+
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
+
mrpoortext_t * mrmsg_get_summary(mrmsg_t *msg, mrchat_t *chat)
Get a summary for a message.
Definition: mrmsg.c:263
+
mrpoortext_t * mrmsg_get_mediainfo(mrmsg_t *msg)
Get real author and title.
Definition: mrmsg.c:480
+
An object representing a single message in memory.
Definition: mrmsg.h:40
+
int mrmsg_show_padlock(mrmsg_t *msg)
Check if a padlock should be shown beside the message.
Definition: mrmsg.c:302
+
uint32_t m_to_id
Contact ID of the receiver, if appropriate.
Definition: mrmsg.h:48
+
uint32_t m_from_id
Contact ID of the sender.
Definition: mrmsg.h:47
+
void mrmsg_set_text(mrmsg_t *msg, const char *text)
Set the text of a message object.
Definition: mrmsg.c:125
+
An object for handling key=value parameter lists.
Definition: mrparam.h:36
+
void mrmsg_unref(mrmsg_t *msg)
Free an mrmsg_t object created eg.
Definition: mrmsg.c:66
+
uint32_t m_id
Message ID.
Definition: mrmsg.h:45
+
void mrmsg_save_param_to_disk(mrmsg_t *msg)
can be used to add some additional, persistent information to a messages record.
Definition: mrmsg.c:590
+
void mrmsg_empty(mrmsg_t *msg)
Empty a message object.
Definition: mrmsg.c:87
+
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
+
An object representing a single chat in memory.
Definition: mrchat.h:39
diff --git a/docs/user/html/mrosnative_8h_source.html b/docs/html/mrosnative_8h_source.html similarity index 98% rename from docs/user/html/mrosnative_8h_source.html rename to docs/html/mrosnative_8h_source.html index 21f1f558..3b28f438 100644 --- a/docs/user/html/mrosnative_8h_source.html +++ b/docs/html/mrosnative_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrosnative.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MROSNATIVE_H__
24 #define __MROSNATIVE_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 
33 typedef struct mrmailbox_t mrmailbox_t;
34 
35 int mrosnative_setup_thread (mrmailbox_t*); /*returns true/false*/
36 void mrosnative_unsetup_thread (mrmailbox_t*);
37 
38 
39 #ifdef __cplusplus
40 } /* /extern "C" */
41 #endif
42 #endif /* __MROSNATIVE_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MROSNATIVE_H__
24 #define __MROSNATIVE_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 
33 typedef struct mrmailbox_t mrmailbox_t;
34 
35 int mrosnative_setup_thread (mrmailbox_t*); /*returns true/false*/
36 void mrosnative_unsetup_thread (mrmailbox_t*);
37 
38 
39 #ifdef __cplusplus
40 } /* /extern "C" */
41 #endif
42 #endif /* __MROSNATIVE_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrparam_8h_source.html b/docs/html/mrparam_8h_source.html similarity index 95% rename from docs/user/html/mrparam_8h_source.html rename to docs/html/mrparam_8h_source.html index a7cb897a..3ad4de46 100644 --- a/docs/user/html/mrparam_8h_source.html +++ b/docs/html/mrparam_8h_source.html @@ -88,19 +88,19 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrparam.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRPARAM_H__
24 #define __MRPARAM_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
36 typedef struct mrparam_t
37 {
39  char* m_packed;
40 } mrparam_t;
41 
42 
43 #define MRP_FILE 'f' /* for msgs */
44 #define MRP_WIDTH 'w' /* for msgs */
45 #define MRP_HEIGHT 'h' /* for msgs */
46 #define MRP_DURATION 'd' /* for msgs */
47 #define MRP_MIMETYPE 'm' /* for msgs */
48 #define MRP_AUTHORNAME 'N' /* for msgs: name of author or artist */
49 #define MRP_TRACKNAME 'n' /* for msgs: name of author or artist */
50 #define MRP_GUARANTEE_E2EE 'c' /* for msgs: 'c'rypted in original/guarantee E2EE or the message is not send */
51 #define MRP_ERRONEOUS_E2EE 'e' /* for msgs: decrypted with validation errors or without mutual set, if neither 'c' nor 'e' are preset, the messages is only transport encrypted */
52 #define MRP_WANTS_MDN 'r' /* for msgs: an incoming message which requestes a MDN (aka read receipt) */
53 #define MRP_FORWARDED 'a' /* for msgs */
54 #define MRP_SYSTEM_CMD 'S' /* for msgs */
55 #define MRP_SYSTEM_CMD_PARAM 'E' /* for msgs */
56 
57 #define MRP_SERVER_FOLDER 'Z' /* for jobs */
58 #define MRP_SERVER_UID 'z' /* for jobs */
59 #define MRP_TIMES 't' /* for jobs: times a job was tried */
60 #define MRP_TIMES_INCREATION 'T' /* for jobs: times a job was tried, used for increation */
61 
62 #define MRP_REFERENCES 'R' /* for groups and chats: References-header last used for a chat */
63 #define MRP_UNPROMOTED 'U' /* for groups */
64 #define MRP_PROFILE_IMAGE 'i' /* for groups and contacts */
65 #define MRP_DEL_AFTER_SEND 'P' /* for groups and msgs: physically delete group after message sending if msg-value matches group-value */
66 
67 
68 /* user functions */
69 int mrparam_exists (mrparam_t*, int key);
70 char* mrparam_get (mrparam_t*, int key, const char* def); /* the value may be an empty string, "def" is returned only if the value unset. The result must be free()'d in any case. */
71 int32_t mrparam_get_int (mrparam_t*, int key, int32_t def);
72 void mrparam_set (mrparam_t*, int key, const char* value);
73 void mrparam_set_int (mrparam_t*, int key, int32_t value);
74 
75 /* library-private */
77 void mrparam_empty (mrparam_t*);
78 void mrparam_unref (mrparam_t*);
79 void mrparam_set_packed (mrparam_t*, const char*);
80 
81 
82 
83 
84 #ifdef __cplusplus
85 } /* /extern "C" */
86 #endif
87 #endif /* __MRPARAM_H__ */
int mrparam_exists(mrparam_t *param, int key)
Check if a parameter exists.
Definition: mrparam.c:161
-
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
-
char * mrparam_get(mrparam_t *param, int key, const char *def)
Get value of a parameter.
Definition: mrparam.c:186
-
void mrparam_set(mrparam_t *param, int key, const char *value)
Set parameter to a string.
Definition: mrparam.c:253
-
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
-
void mrparam_set_int(mrparam_t *param, int key, int32_t value)
Set parameter to an integer.
Definition: mrparam.c:318
-
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
-
void mrparam_empty(mrparam_t *param)
Delete all parameters.
Definition: mrparam.c:111
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRPARAM_H__
24 #define __MRPARAM_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
36 typedef struct mrparam_t
37 {
39  char* m_packed;
40 } mrparam_t;
41 
42 
43 #define MRP_FILE 'f' /* for msgs */
44 #define MRP_WIDTH 'w' /* for msgs */
45 #define MRP_HEIGHT 'h' /* for msgs */
46 #define MRP_DURATION 'd' /* for msgs */
47 #define MRP_MIMETYPE 'm' /* for msgs */
48 #define MRP_AUTHORNAME 'N' /* for msgs: name of author or artist */
49 #define MRP_TRACKNAME 'n' /* for msgs: name of author or artist */
50 #define MRP_GUARANTEE_E2EE 'c' /* for msgs: 'c'rypted in original/guarantee E2EE or the message is not send */
51 #define MRP_ERRONEOUS_E2EE 'e' /* for msgs: decrypted with validation errors or without mutual set, if neither 'c' nor 'e' are preset, the messages is only transport encrypted */
52 #define MRP_WANTS_MDN 'r' /* for msgs: an incoming message which requestes a MDN (aka read receipt) */
53 #define MRP_FORWARDED 'a' /* for msgs */
54 #define MRP_SYSTEM_CMD 'S' /* for msgs */
55 #define MRP_SYSTEM_CMD_PARAM 'E' /* for msgs */
56 
57 #define MRP_SERVER_FOLDER 'Z' /* for jobs */
58 #define MRP_SERVER_UID 'z' /* for jobs */
59 #define MRP_TIMES 't' /* for jobs: times a job was tried */
60 #define MRP_TIMES_INCREATION 'T' /* for jobs: times a job was tried, used for increation */
61 
62 #define MRP_REFERENCES 'R' /* for groups and chats: References-header last used for a chat */
63 #define MRP_UNPROMOTED 'U' /* for groups */
64 #define MRP_PROFILE_IMAGE 'i' /* for groups and contacts */
65 #define MRP_DEL_AFTER_SEND 'P' /* for groups and msgs: physically delete group after message sending if msg-value matches group-value */
66 
67 
68 /* user functions */
69 int mrparam_exists (mrparam_t*, int key);
70 char* mrparam_get (mrparam_t*, int key, const char* def); /* the value may be an empty string, "def" is returned only if the value unset. The result must be free()'d in any case. */
71 int32_t mrparam_get_int (mrparam_t*, int key, int32_t def);
72 void mrparam_set (mrparam_t*, int key, const char* value);
73 void mrparam_set_int (mrparam_t*, int key, int32_t value);
74 
75 /* library-private */
77 void mrparam_empty (mrparam_t*);
78 void mrparam_unref (mrparam_t*);
79 void mrparam_set_packed (mrparam_t*, const char*);
80 
81 
82 
83 
84 #ifdef __cplusplus
85 } /* /extern "C" */
86 #endif
87 #endif /* __MRPARAM_H__ */
int mrparam_exists(mrparam_t *param, int key)
Check if a parameter exists.
Definition: mrparam.c:161
+
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
+
char * mrparam_get(mrparam_t *param, int key, const char *def)
Get value of a parameter.
Definition: mrparam.c:186
+
void mrparam_set(mrparam_t *param, int key, const char *value)
Set parameter to a string.
Definition: mrparam.c:253
+
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
+
void mrparam_set_int(mrparam_t *param, int key, int32_t value)
Set parameter to an integer.
Definition: mrparam.c:318
+
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
+
An object for handling key=value parameter lists.
Definition: mrparam.h:36
+
void mrparam_empty(mrparam_t *param)
Delete all parameters.
Definition: mrparam.c:111
diff --git a/docs/user/html/mrpgp_8h_source.html b/docs/html/mrpgp_8h_source.html similarity index 98% rename from docs/user/html/mrpgp_8h_source.html rename to docs/html/mrpgp_8h_source.html index e99d2196..d13a46da 100644 --- a/docs/user/html/mrpgp_8h_source.html +++ b/docs/html/mrpgp_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrpgp.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRPGP_H__
24 #define __MRPGP_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 typedef struct mrkey_t mrkey_t;
33 typedef struct mrkeyring_t mrkeyring_t;
34 
35 
36 /* validation errors */
37 #define MR_VALIDATE_NO_SIGNATURE 0x01
38 #define MR_VALIDATE_UNKNOWN_SIGNATURE 0x02
39 #define MR_VALIDATE_BAD_SIGNATURE 0x04
40 #define MR_VALIDATE_NOT_MUTUAL 0x08
41 
42 /* misc. */
43 void mrpgp_init (mrmailbox_t*);
44 void mrpgp_exit (mrmailbox_t*);
45 void mrpgp_rand_seed (mrmailbox_t*, const void* buf, size_t bytes);
46 
47 
48 /* public key encryption */
49 int mrpgp_create_keypair (mrmailbox_t*, const char* addr, mrkey_t* public_key, mrkey_t* private_key);
50 int mrpgp_is_valid_key (mrmailbox_t*, const mrkey_t*);
51 int mrpgp_calc_fingerprint (mrmailbox_t*, const mrkey_t*, uint8_t** fingerprint, size_t* fingerprint_bytes);
52 int mrpgp_split_key (mrmailbox_t*, const mrkey_t* private_in, mrkey_t* public_out);
53 
54 int mrpgp_pk_encrypt (mrmailbox_t*, const void* plain, size_t plain_bytes, const mrkeyring_t*, const mrkey_t* sign_key, int use_armor, void** ret_ctext, size_t* ret_ctext_bytes);
55 int mrpgp_pk_decrypt (mrmailbox_t*, const void* ctext, size_t ctext_bytes, const mrkeyring_t*, const mrkey_t* validate_key, int use_armor, void** plain, size_t* plain_bytes, int* ret_validation_errors);
56 
57 
58 #ifdef __cplusplus
59 } /* /extern "C" */
60 #endif
61 #endif /* __MRPGP_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRPGP_H__
24 #define __MRPGP_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 typedef struct mrkey_t mrkey_t;
33 typedef struct mrkeyring_t mrkeyring_t;
34 
35 
36 /* validation errors */
37 #define MR_VALIDATE_NO_SIGNATURE 0x01
38 #define MR_VALIDATE_UNKNOWN_SIGNATURE 0x02
39 #define MR_VALIDATE_BAD_SIGNATURE 0x04
40 #define MR_VALIDATE_NOT_MUTUAL 0x08
41 
42 /* misc. */
43 void mrpgp_init (mrmailbox_t*);
44 void mrpgp_exit (mrmailbox_t*);
45 void mrpgp_rand_seed (mrmailbox_t*, const void* buf, size_t bytes);
46 
47 
48 /* public key encryption */
49 int mrpgp_create_keypair (mrmailbox_t*, const char* addr, mrkey_t* public_key, mrkey_t* private_key);
50 int mrpgp_is_valid_key (mrmailbox_t*, const mrkey_t*);
51 int mrpgp_calc_fingerprint (mrmailbox_t*, const mrkey_t*, uint8_t** fingerprint, size_t* fingerprint_bytes);
52 int mrpgp_split_key (mrmailbox_t*, const mrkey_t* private_in, mrkey_t* public_out);
53 
54 int mrpgp_pk_encrypt (mrmailbox_t*, const void* plain, size_t plain_bytes, const mrkeyring_t*, const mrkey_t* sign_key, int use_armor, void** ret_ctext, size_t* ret_ctext_bytes);
55 int mrpgp_pk_decrypt (mrmailbox_t*, const void* ctext, size_t ctext_bytes, const mrkeyring_t*, const mrkey_t* validate_key, int use_armor, void** plain, size_t* plain_bytes, int* ret_validation_errors);
56 
57 
58 #ifdef __cplusplus
59 } /* /extern "C" */
60 #endif
61 #endif /* __MRPGP_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrpoortext_8h_source.html b/docs/html/mrpoortext_8h_source.html similarity index 93% rename from docs/user/html/mrpoortext_8h_source.html rename to docs/html/mrpoortext_8h_source.html index 01683b1f..a6b4c14b 100644 --- a/docs/user/html/mrpoortext_8h_source.html +++ b/docs/html/mrpoortext_8h_source.html @@ -88,20 +88,20 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrpoortext.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRPOORTEXT_H__
24 #define __MRPOORTEXT_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
35 typedef struct mrpoortext_t
36 {
38  char* m_text1;
39  char* m_text2;
40  time_t m_timestamp;
41  int m_state;
42 } mrpoortext_t;
43 
44 
45 #define MR_TEXT1_NORMAL 0
46 #define MR_TEXT1_DRAFT 1
47 #define MR_TEXT1_USERNAME 2
48 #define MR_TEXT1_SELF 3
51 mrpoortext_t* mrpoortext_new ();
52 void mrpoortext_empty (mrpoortext_t*);
54 
55 
56 #define MR_SUMMARY_CHARACTERS 160 /* in practice, the user additionally cuts the string himself pixel-accurate */
57 void mrpoortext_fill (mrpoortext_t*, const mrmsg_t*, const mrchat_t*, const mrcontact_t*);
58 
59 
60 #ifdef __cplusplus
61 } /* /extern "C" */
62 #endif
63 #endif /* __MRPOORTEXT_H__ */
void mrpoortext_unref(mrpoortext_t *poortext)
Frees a mrpoortext_t object created eg.
Definition: mrpoortext.c:55
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
char * m_text2
may be NULL
Definition: mrpoortext.h:39
-
int m_state
may be 0
Definition: mrpoortext.h:41
-
time_t m_timestamp
may be 0
Definition: mrpoortext.h:40
-
char * m_text1
may be NULL
Definition: mrpoortext.h:38
-
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
int m_text1_meaning
One of MR_TEXT1_NORMAL, MR_TEXT1_DRAFT, MR_TEXT1_USERNAME or MR_TEXT1_SELF.
Definition: mrpoortext.h:37
-
An object representing a single chat in memory.
Definition: mrchat.h:39
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRPOORTEXT_H__
24 #define __MRPOORTEXT_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
35 typedef struct mrpoortext_t
36 {
38  char* m_text1;
39  char* m_text2;
40  time_t m_timestamp;
41  int m_state;
42 } mrpoortext_t;
43 
44 
45 #define MR_TEXT1_NORMAL 0
46 #define MR_TEXT1_DRAFT 1
47 #define MR_TEXT1_USERNAME 2
48 #define MR_TEXT1_SELF 3
51 mrpoortext_t* mrpoortext_new ();
52 void mrpoortext_empty (mrpoortext_t*);
54 
55 
56 #define MR_SUMMARY_CHARACTERS 160 /* in practice, the user additionally cuts the string himself pixel-accurate */
57 void mrpoortext_fill (mrpoortext_t*, const mrmsg_t*, const mrchat_t*, const mrcontact_t*);
58 
59 
60 #ifdef __cplusplus
61 } /* /extern "C" */
62 #endif
63 #endif /* __MRPOORTEXT_H__ */
void mrpoortext_unref(mrpoortext_t *poortext)
Frees a mrpoortext_t object created eg.
Definition: mrpoortext.c:55
+
An object representing a single contact in memory.
Definition: mrcontact.h:38
+
char * m_text2
may be NULL
Definition: mrpoortext.h:39
+
int m_state
may be 0
Definition: mrpoortext.h:41
+
time_t m_timestamp
may be 0
Definition: mrpoortext.h:40
+
char * m_text1
may be NULL
Definition: mrpoortext.h:38
+
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
+
An object representing a single message in memory.
Definition: mrmsg.h:40
+
int m_text1_meaning
One of MR_TEXT1_NORMAL, MR_TEXT1_DRAFT, MR_TEXT1_USERNAME or MR_TEXT1_SELF.
Definition: mrpoortext.h:37
+
An object representing a single chat in memory.
Definition: mrchat.h:39
diff --git a/docs/user/html/mrsaxparser_8h_source.html b/docs/html/mrsaxparser_8h_source.html similarity index 99% rename from docs/user/html/mrsaxparser_8h_source.html rename to docs/html/mrsaxparser_8h_source.html index 413fff73..68351d1a 100644 --- a/docs/user/html/mrsaxparser_8h_source.html +++ b/docs/html/mrsaxparser_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSAXPARSER_H__
24 #define __MRSAXPARSER_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 typedef void (*mrsaxparser_starttag_cb_t) (void* userdata, const char* tag, char** attr);
31 typedef void (*mrsaxparser_endtag_cb_t) (void* userdata, const char* tag);
32 typedef void (*mrsaxparser_text_cb_t) (void* userdata, const char* text, int len); /* len is only informational, text is already null-terminated */
33 
34 
35 typedef struct mrsaxparser_t
36 {
37  mrsaxparser_starttag_cb_t m_starttag_cb;
38  mrsaxparser_endtag_cb_t m_endtag_cb;
39  mrsaxparser_text_cb_t m_text_cb;
40  void* m_userdata;
41 } mrsaxparser_t;
42 
43 
44 void mrsaxparser_init (mrsaxparser_t*, void* userData);
45 void mrsaxparser_set_tag_handler (mrsaxparser_t*, mrsaxparser_starttag_cb_t, mrsaxparser_endtag_cb_t);
46 void mrsaxparser_set_text_handler (mrsaxparser_t*, mrsaxparser_text_cb_t);
47 
48 void mrsaxparser_parse (mrsaxparser_t*, const char* text);
49 
50 const char* mrattr_find (char** attr, const char* key);
51 
52 
53 /*** library-private **********************************************************/
54 
55 
56 #ifdef __cplusplus
57 } /* /extern "C" */
58 #endif
59 #endif /* __MRSAXPARSER_H__ */
60 
diff --git a/docs/user/html/mrsimplify_8h_source.html b/docs/html/mrsimplify_8h_source.html similarity index 99% rename from docs/user/html/mrsimplify_8h_source.html rename to docs/html/mrsimplify_8h_source.html index 35fe8ec7..440d8ad5 100644 --- a/docs/user/html/mrsimplify_8h_source.html +++ b/docs/html/mrsimplify_8h_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSIMPLIFY_H__
24 #define __MRSIMPLIFY_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 typedef struct mrsimplify_t
33 {
34  int m_is_forwarded;
35 } mrsimplify_t;
36 
37 
38 mrsimplify_t* mrsimplify_new ();
39 void mrsimplify_unref (mrsimplify_t*);
40 
41 /* Simplify and normalise text: Remove quotes, signatures, unnecessary
42 lineends etc.
43 The data returned from Simplify() must be free()'d when no longer used, private */
44 char* mrsimplify_simplify (mrsimplify_t*, const char* txt_unterminated, int txt_bytes, int is_html);
45 
46 
47 #ifdef __cplusplus
48 } /* /extern "C" */
49 #endif
50 #endif /* __MRSIMPLIFY_H__ */
51 
diff --git a/docs/user/html/mrsmtp_8h_source.html b/docs/html/mrsmtp_8h_source.html similarity index 98% rename from docs/user/html/mrsmtp_8h_source.html rename to docs/html/mrsmtp_8h_source.html index d6fde222..64323eaa 100644 --- a/docs/user/html/mrsmtp_8h_source.html +++ b/docs/html/mrsmtp_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrsmtp.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSMTP_H__
24 #define __MRSMTP_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #include "mrloginparam.h"
31 
32 
33 /*** library-private **********************************************************/
34 
35 typedef struct mrsmtp_t
36 {
37  mailsmtp* m_hEtpan;
38  char* m_from;
39  int m_esmtp;
40  pthread_mutex_t m_mutex;
41 
42  int m_log_connect_errors;
43  int m_log_usual_error;
44 
45  mrmailbox_t* m_mailbox; /* only for logging! */
46 } mrsmtp_t;
47 
48 mrsmtp_t* mrsmtp_new (mrmailbox_t*);
49 void mrsmtp_unref (mrsmtp_t*);
50 int mrsmtp_is_connected (const mrsmtp_t*);
51 int mrsmtp_connect (mrsmtp_t*, const mrloginparam_t*);
52 void mrsmtp_disconnect (mrsmtp_t*);
53 int mrsmtp_send_msg (mrsmtp_t*, const clist* recipients, const char* data, size_t data_bytes);
54 
55 
56 #ifdef __cplusplus
57 } /* /extern "C" */
58 #endif
59 #endif /* __MRPARAM_H__ */
60 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSMTP_H__
24 #define __MRSMTP_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #include "mrloginparam.h"
31 
32 
33 /*** library-private **********************************************************/
34 
35 typedef struct mrsmtp_t
36 {
37  mailsmtp* m_hEtpan;
38  char* m_from;
39  int m_esmtp;
40  pthread_mutex_t m_mutex;
41 
42  int m_log_connect_errors;
43  int m_log_usual_error;
44 
45  mrmailbox_t* m_mailbox; /* only for logging! */
46 } mrsmtp_t;
47 
48 mrsmtp_t* mrsmtp_new (mrmailbox_t*);
49 void mrsmtp_unref (mrsmtp_t*);
50 int mrsmtp_is_connected (const mrsmtp_t*);
51 int mrsmtp_connect (mrsmtp_t*, const mrloginparam_t*);
52 void mrsmtp_disconnect (mrsmtp_t*);
53 int mrsmtp_send_msg (mrsmtp_t*, const clist* recipients, const char* data, size_t data_bytes);
54 
55 
56 #ifdef __cplusplus
57 } /* /extern "C" */
58 #endif
59 #endif /* __MRPARAM_H__ */
60 
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrsqlite3_8h_source.html b/docs/html/mrsqlite3_8h_source.html similarity index 99% rename from docs/user/html/mrsqlite3_8h_source.html rename to docs/html/mrsqlite3_8h_source.html index d0b28838..fff61c1d 100644 --- a/docs/user/html/mrsqlite3_8h_source.html +++ b/docs/html/mrsqlite3_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrsqlite3.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSQLITE3_H__
24 #define __MRSQLITE3_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 #include <sqlite3.h>
33 #include <libetpan/libetpan.h>
34 #include <pthread.h>
35 typedef struct mrmailbox_t mrmailbox_t;
36 
37 
38 /* predefined statements */
39 enum
40 {
41  BEGIN_transaction = 0 /* must be first */
42  ,ROLLBACK_transaction
43  ,COMMIT_transaction
44 
45  ,SELECT_v_FROM_config_k
46  ,INSERT_INTO_config_kv
47  ,UPDATE_config_vk
48  ,DELETE_FROM_config_k
49 
50  ,SELECT_COUNT_FROM_contacts
51  ,SELECT_naob_FROM_contacts_i
52  ,SELECT_inao_FROM_contacts_a
53  ,SELECT_id_FROM_contacts_WHERE_id
54  ,SELECT_na_FROM_chats_contacs_JOIN_contacts_WHERE_cc
55  ,SELECT_p_FROM_chats_contacs_JOIN_contacts_peerstates_WHERE_cc
56  ,SELECT_id_FROM_contacts_WHERE_chat_id
57  ,SELECT_id_FROM_contacts_ORDER_BY
58  ,SELECT_id_FROM_contacts_WHERE_query_ORDER_BY
59  ,SELECT_COUNT_FROM_contacts_WHERE_blocked
60  ,SELECT_id_FROM_contacts_WHERE_blocked
61  ,INSERT_INTO_contacts_neo
62  ,UPDATE_contacts_nao_WHERE_i
63  ,UPDATE_contacts_SET_origin_WHERE_id
64  ,UPDATE_contacts_SET_b_WHERE_i
65  ,DELETE_FROM_contacts_WHERE_id
66 
67  ,SELECT_COUNT_FROM_chats
68  ,SELECT_COUNT_FROM_chats_WHERE_archived
69  ,SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_archived
70  ,SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_unarchived
71  ,SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_query
72  ,SELECT_itndd_FROM_chats_WHERE_i
73  ,SELECT_id_FROM_chats_WHERE_id
74  ,SELECT_id_FROM_chats_WHERE_contact_id
75  ,SELECT_id_FROM_CHATS_WHERE_grpid
76  ,SELECT_timestamp_FROM_msgs_WHERE_timestamp
77  ,SELECT_it_FROM_msgs_JOIN_chats_WHERE_rfc724
78  ,SELECT_MAX_timestamp_FROM_msgs
79  ,SELECT_rfc724_FROM_msgs_ORDER_BY_timestamp_LIMIT_1
80  ,UPDATE_chats_SET_draft_WHERE_id
81  ,UPDATE_chats_SET_n_WHERE_c
82  ,UPDATE_chats_SET_blocked
83  ,UPDATE_chats_SET_unarchived
84 
85  ,SELECT_a_FROM_chats_contacts_WHERE_i
86  ,SELECT_COUNT_FROM_chats_contacts_WHERE_chat_id
87  ,SELECT_COUNT_FROM_chats_contacts_WHERE_contact_id
88  ,SELECT_c_FROM_chats_contacts_WHERE_c
89  ,SELECT_c_FROM_chats_contacts_WHERE_c_ORDER_BY
90  ,SELECT_void_FROM_chats_contacts_WHERE_chat_id_AND_contact_id
91  ,INSERT_INTO_chats_contacts
92 
93  ,SELECT_COUNT_FROM_msgs_WHERE_assigned
94  ,SELECT_COUNT_FROM_msgs_WHERE_unassigned
95  ,SELECT_COUNT_FROM_msgs_WHERE_state_AND_chat_id
96  ,SELECT_COUNT_FROM_msgs_WHERE_chat_id
97  ,SELECT_COUNT_FROM_msgs_WHERE_rfc724_mid
98  ,SELECT_COUNT_FROM_msgs_WHERE_ft
99  ,SELECT_COUNT_DISTINCT_f_FROM_msgs_WHERE_c
100  ,SELECT_i_FROM_msgs_WHERE_ctt
101  ,SELECT_id_FROM_msgs_WHERE_cm
102  ,SELECT_id_FROM_msgs_WHERE_mcm
103  ,SELECT_id_FROM_msgs_WHERE_fresh_AND_deaddrop
104  ,SELECT_txt_raw_FROM_msgs_WHERE_id
105  ,SELECT_ircftttstpb_FROM_msg_WHERE_i
106  ,SELECT_ss_FROM_msgs_WHERE_m
107  ,SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_c
108  ,SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_starred
109  ,SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_fresh
110  ,SELECT_i_FROM_msgs_WHERE_query
111  ,SELECT_i_FROM_msgs_WHERE_chat_id_AND_query
112  ,INSERT_INTO_msgs_msscftttsmttpb
113  ,INSERT_INTO_msgs_mcftttstpb
114  ,UPDATE_msgs_SET_chat_id_WHERE_id
115  ,UPDATE_msgs_SET_state_WHERE_id
116  ,UPDATE_msgs_SET_seen_WHERE_id_AND_chat_id_AND_freshORnoticed
117  ,UPDATE_msgs_SET_noticed_WHERE_id_AND_fresh
118  ,UPDATE_msgs_SET_state_WHERE_chat_id_AND_state
119  ,UPDATE_msgs_SET_state_WHERE_from_id_AND_state
120  ,UPDATE_msgs_SET_ss_WHERE_rfc724_mid
121  ,UPDATE_msgs_SET_param_WHERE_id
122  ,UPDATE_msgs_SET_starred_WHERE_id
123  ,DELETE_FROM_msgs_WHERE_id
124  ,DELETE_FROM_msgs_WHERE_rfc724_mid
125 
126  ,SELECT_c_FROM_msgs_mdns_WHERE_mc
127  ,INSERT_INTO_msgs_mdns
128  ,SELECT_COUNT_FROM_msgs_mdns_WHERE_m
129  ,DELETE_FROM_msgs_mdns_WHERE_m
130 
131  ,INSERT_INTO_jobs_aafp
132  ,SELECT_MIN_d_FROM_jobs
133  ,SELECT_iafp_FROM_jobs
134  ,DELETE_FROM_jobs_WHERE_id
135  ,DELETE_FROM_jobs_WHERE_action
136  ,UPDATE_jobs_SET_dp_WHERE_id
137 
138  ,SELECT_FROM_leftgrps_WHERE_grpid
139 
140  ,INSERT_INTO_acpeerstates_a
141  ,SELECT_aclpp_FROM_acpeerstates_WHERE_a
142  ,UPDATE_acpeerstates_SET_l_WHERE_a
143  ,UPDATE_acpeerstates_SET_lcpp_WHERE_a
144 
145  ,INSERT_INTO_keypairs_aippc
146  ,SELECT_private_key_FROM_keypairs_WHERE_default
147  ,SELECT_private_key_FROM_keypairs_ORDER_BY_default
148  ,SELECT_public_key_FROM_keypairs_WHERE_default
149 
150  ,PREDEFINED_CNT /* must be last */
151 };
152 
153 
162 typedef struct mrsqlite3_t
163 {
165  sqlite3_stmt* m_pd[PREDEFINED_CNT];
166  sqlite3* m_cobj;
167  int m_transactionCount;
168  mrmailbox_t* m_mailbox;
169  pthread_mutex_t m_critical_;
171 } mrsqlite3_t;
172 
173 
174 mrsqlite3_t* mrsqlite3_new (mrmailbox_t*);
175 void mrsqlite3_unref (mrsqlite3_t*);
176 
177 #define MR_OPEN_READONLY 0x01
178 int mrsqlite3_open__ (mrsqlite3_t*, const char* dbfile, int flags);
179 
180 void mrsqlite3_close__ (mrsqlite3_t*);
181 int mrsqlite3_is_open (const mrsqlite3_t*);
182 
183 /* handle configurations, private */
184 int mrsqlite3_set_config__ (mrsqlite3_t*, const char* key, const char* value);
185 int mrsqlite3_set_config_int__ (mrsqlite3_t*, const char* key, int32_t value);
186 char* mrsqlite3_get_config__ (mrsqlite3_t*, const char* key, const char* def); /* the returned string must be free()'d, returns NULL on errors */
187 int32_t mrsqlite3_get_config_int__ (mrsqlite3_t*, const char* key, int32_t def);
188 
189 /* tools, these functions are compatible to the corresponding sqlite3_* functions */
190 sqlite3_stmt* mrsqlite3_predefine__ (mrsqlite3_t*, size_t idx, const char* sql); /*the result is resetted as needed and must not be freed. CAVE: you must not call this function with different strings for the same index!*/
191 sqlite3_stmt* mrsqlite3_prepare_v2_ (mrsqlite3_t*, const char* sql); /* the result mus be freed using sqlite3_finalize() */
192 int mrsqlite3_execute__ (mrsqlite3_t*, const char* sql);
193 int mrsqlite3_table_exists__ (mrsqlite3_t*, const char* name);
194 void mrsqlite3_log_error (mrsqlite3_t*, const char* msg, ...);
195 
196 /* reset all predefined statements, this is needed only in very rare cases, eg. when dropping a table and there are pending statements */
197 void mrsqlite3_reset_all_predefinitions(mrsqlite3_t*);
198 
199 /* tools for locking, may be called nested, see also m_critical_ above.
200 the user of MrSqlite3 must make sure that the MrSqlite3-object is only used by one thread at the same time.
201 In general, we will lock the hightest level as possible - this avoids deadlocks and massive on/off lockings.
202 Low-level-functions, eg. the MrSqlite3-methods, do not lock. */
203 void mrsqlite3_lock (mrsqlite3_t*); /* lock or wait; these calls must not be nested in a single thread */
204 void mrsqlite3_unlock (mrsqlite3_t*);
205 
206 /* nestable transactions, only the outest is really used */
207 void mrsqlite3_begin_transaction__(mrsqlite3_t*);
208 void mrsqlite3_commit__ (mrsqlite3_t*);
209 void mrsqlite3_rollback__ (mrsqlite3_t*);
210 
211 #ifdef __cplusplus
212 } /* /extern "C" */
213 #endif
214 #endif /* __MRSQLITE3_H__ */
215 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSQLITE3_H__
24 #define __MRSQLITE3_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 /*** library-private **********************************************************/
31 
32 #include <sqlite3.h>
33 #include <libetpan/libetpan.h>
34 #include <pthread.h>
35 typedef struct mrmailbox_t mrmailbox_t;
36 
37 
38 /* predefined statements */
39 enum
40 {
41  BEGIN_transaction = 0 /* must be first */
42  ,ROLLBACK_transaction
43  ,COMMIT_transaction
44 
45  ,SELECT_v_FROM_config_k
46  ,INSERT_INTO_config_kv
47  ,UPDATE_config_vk
48  ,DELETE_FROM_config_k
49 
50  ,SELECT_COUNT_FROM_contacts
51  ,SELECT_naob_FROM_contacts_i
52  ,SELECT_inao_FROM_contacts_a
53  ,SELECT_id_FROM_contacts_WHERE_id
54  ,SELECT_na_FROM_chats_contacs_JOIN_contacts_WHERE_cc
55  ,SELECT_p_FROM_chats_contacs_JOIN_contacts_peerstates_WHERE_cc
56  ,SELECT_id_FROM_contacts_WHERE_chat_id
57  ,SELECT_id_FROM_contacts_ORDER_BY
58  ,SELECT_id_FROM_contacts_WHERE_query_ORDER_BY
59  ,SELECT_COUNT_FROM_contacts_WHERE_blocked
60  ,SELECT_id_FROM_contacts_WHERE_blocked
61  ,INSERT_INTO_contacts_neo
62  ,UPDATE_contacts_nao_WHERE_i
63  ,UPDATE_contacts_SET_origin_WHERE_id
64  ,UPDATE_contacts_SET_b_WHERE_i
65  ,DELETE_FROM_contacts_WHERE_id
66 
67  ,SELECT_COUNT_FROM_chats
68  ,SELECT_COUNT_FROM_chats_WHERE_archived
69  ,SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_archived
70  ,SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_unarchived
71  ,SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_query
72  ,SELECT_itndd_FROM_chats_WHERE_i
73  ,SELECT_id_FROM_chats_WHERE_id
74  ,SELECT_id_FROM_chats_WHERE_contact_id
75  ,SELECT_id_FROM_CHATS_WHERE_grpid
76  ,SELECT_timestamp_FROM_msgs_WHERE_timestamp
77  ,SELECT_it_FROM_msgs_JOIN_chats_WHERE_rfc724
78  ,SELECT_MAX_timestamp_FROM_msgs
79  ,SELECT_rfc724_FROM_msgs_ORDER_BY_timestamp_LIMIT_1
80  ,UPDATE_chats_SET_draft_WHERE_id
81  ,UPDATE_chats_SET_n_WHERE_c
82  ,UPDATE_chats_SET_blocked
83  ,UPDATE_chats_SET_unarchived
84 
85  ,SELECT_a_FROM_chats_contacts_WHERE_i
86  ,SELECT_COUNT_FROM_chats_contacts_WHERE_chat_id
87  ,SELECT_COUNT_FROM_chats_contacts_WHERE_contact_id
88  ,SELECT_c_FROM_chats_contacts_WHERE_c
89  ,SELECT_c_FROM_chats_contacts_WHERE_c_ORDER_BY
90  ,SELECT_void_FROM_chats_contacts_WHERE_chat_id_AND_contact_id
91  ,INSERT_INTO_chats_contacts
92 
93  ,SELECT_COUNT_FROM_msgs_WHERE_assigned
94  ,SELECT_COUNT_FROM_msgs_WHERE_unassigned
95  ,SELECT_COUNT_FROM_msgs_WHERE_state_AND_chat_id
96  ,SELECT_COUNT_FROM_msgs_WHERE_chat_id
97  ,SELECT_COUNT_FROM_msgs_WHERE_rfc724_mid
98  ,SELECT_COUNT_FROM_msgs_WHERE_ft
99  ,SELECT_COUNT_DISTINCT_f_FROM_msgs_WHERE_c
100  ,SELECT_i_FROM_msgs_WHERE_ctt
101  ,SELECT_id_FROM_msgs_WHERE_cm
102  ,SELECT_id_FROM_msgs_WHERE_mcm
103  ,SELECT_id_FROM_msgs_WHERE_fresh_AND_deaddrop
104  ,SELECT_txt_raw_FROM_msgs_WHERE_id
105  ,SELECT_ircftttstpb_FROM_msg_WHERE_i
106  ,SELECT_ss_FROM_msgs_WHERE_m
107  ,SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_c
108  ,SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_starred
109  ,SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_fresh
110  ,SELECT_i_FROM_msgs_WHERE_query
111  ,SELECT_i_FROM_msgs_WHERE_chat_id_AND_query
112  ,INSERT_INTO_msgs_msscftttsmttpb
113  ,INSERT_INTO_msgs_mcftttstpb
114  ,UPDATE_msgs_SET_chat_id_WHERE_id
115  ,UPDATE_msgs_SET_state_WHERE_id
116  ,UPDATE_msgs_SET_seen_WHERE_id_AND_chat_id_AND_freshORnoticed
117  ,UPDATE_msgs_SET_noticed_WHERE_id_AND_fresh
118  ,UPDATE_msgs_SET_state_WHERE_chat_id_AND_state
119  ,UPDATE_msgs_SET_state_WHERE_from_id_AND_state
120  ,UPDATE_msgs_SET_ss_WHERE_rfc724_mid
121  ,UPDATE_msgs_SET_param_WHERE_id
122  ,UPDATE_msgs_SET_starred_WHERE_id
123  ,DELETE_FROM_msgs_WHERE_id
124  ,DELETE_FROM_msgs_WHERE_rfc724_mid
125 
126  ,SELECT_c_FROM_msgs_mdns_WHERE_mc
127  ,INSERT_INTO_msgs_mdns
128  ,SELECT_COUNT_FROM_msgs_mdns_WHERE_m
129  ,DELETE_FROM_msgs_mdns_WHERE_m
130 
131  ,INSERT_INTO_jobs_aafp
132  ,SELECT_MIN_d_FROM_jobs
133  ,SELECT_iafp_FROM_jobs
134  ,DELETE_FROM_jobs_WHERE_id
135  ,DELETE_FROM_jobs_WHERE_action
136  ,UPDATE_jobs_SET_dp_WHERE_id
137 
138  ,SELECT_FROM_leftgrps_WHERE_grpid
139 
140  ,INSERT_INTO_acpeerstates_a
141  ,SELECT_aclpp_FROM_acpeerstates_WHERE_a
142  ,UPDATE_acpeerstates_SET_l_WHERE_a
143  ,UPDATE_acpeerstates_SET_lcpp_WHERE_a
144 
145  ,INSERT_INTO_keypairs_aippc
146  ,SELECT_private_key_FROM_keypairs_WHERE_default
147  ,SELECT_private_key_FROM_keypairs_ORDER_BY_default
148  ,SELECT_public_key_FROM_keypairs_WHERE_default
149 
150  ,PREDEFINED_CNT /* must be last */
151 };
152 
153 
162 typedef struct mrsqlite3_t
163 {
165  sqlite3_stmt* m_pd[PREDEFINED_CNT];
166  sqlite3* m_cobj;
167  int m_transactionCount;
168  mrmailbox_t* m_mailbox;
169  pthread_mutex_t m_critical_;
171 } mrsqlite3_t;
172 
173 
174 mrsqlite3_t* mrsqlite3_new (mrmailbox_t*);
175 void mrsqlite3_unref (mrsqlite3_t*);
176 
177 #define MR_OPEN_READONLY 0x01
178 int mrsqlite3_open__ (mrsqlite3_t*, const char* dbfile, int flags);
179 
180 void mrsqlite3_close__ (mrsqlite3_t*);
181 int mrsqlite3_is_open (const mrsqlite3_t*);
182 
183 /* handle configurations, private */
184 int mrsqlite3_set_config__ (mrsqlite3_t*, const char* key, const char* value);
185 int mrsqlite3_set_config_int__ (mrsqlite3_t*, const char* key, int32_t value);
186 char* mrsqlite3_get_config__ (mrsqlite3_t*, const char* key, const char* def); /* the returned string must be free()'d, returns NULL on errors */
187 int32_t mrsqlite3_get_config_int__ (mrsqlite3_t*, const char* key, int32_t def);
188 
189 /* tools, these functions are compatible to the corresponding sqlite3_* functions */
190 sqlite3_stmt* mrsqlite3_predefine__ (mrsqlite3_t*, size_t idx, const char* sql); /*the result is resetted as needed and must not be freed. CAVE: you must not call this function with different strings for the same index!*/
191 sqlite3_stmt* mrsqlite3_prepare_v2_ (mrsqlite3_t*, const char* sql); /* the result mus be freed using sqlite3_finalize() */
192 int mrsqlite3_execute__ (mrsqlite3_t*, const char* sql);
193 int mrsqlite3_table_exists__ (mrsqlite3_t*, const char* name);
194 void mrsqlite3_log_error (mrsqlite3_t*, const char* msg, ...);
195 
196 /* reset all predefined statements, this is needed only in very rare cases, eg. when dropping a table and there are pending statements */
197 void mrsqlite3_reset_all_predefinitions(mrsqlite3_t*);
198 
199 /* tools for locking, may be called nested, see also m_critical_ above.
200 the user of MrSqlite3 must make sure that the MrSqlite3-object is only used by one thread at the same time.
201 In general, we will lock the hightest level as possible - this avoids deadlocks and massive on/off lockings.
202 Low-level-functions, eg. the MrSqlite3-methods, do not lock. */
203 void mrsqlite3_lock (mrsqlite3_t*); /* lock or wait; these calls must not be nested in a single thread */
204 void mrsqlite3_unlock (mrsqlite3_t*);
205 
206 /* nestable transactions, only the outest is really used */
207 void mrsqlite3_begin_transaction__(mrsqlite3_t*);
208 void mrsqlite3_commit__ (mrsqlite3_t*);
209 void mrsqlite3_rollback__ (mrsqlite3_t*);
210 
211 #ifdef __cplusplus
212 } /* /extern "C" */
213 #endif
214 #endif /* __MRSQLITE3_H__ */
215 
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrstock_8h_source.html b/docs/html/mrstock_8h_source.html similarity index 99% rename from docs/user/html/mrstock_8h_source.html rename to docs/html/mrstock_8h_source.html index 1aaa9775..54fb1024 100644 --- a/docs/user/html/mrstock_8h_source.html +++ b/docs/html/mrstock_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrstock.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSTOCK_H__
24 #define __MRSTOCK_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #include "mrmailbox.h"
31 #include <stdlib.h>
32 #include <string.h>
33 
34 
35 /* Strings requested by MR_EVENT_GET_STRING and MR_EVENT_GET_QUANTITY_STRING */
36 #define MR_STR_FREE_ 0
37 #define MR_STR_NOMESSAGES 1
38 #define MR_STR_SELF 2
39 #define MR_STR_DRAFT 3
40 #define MR_STR_MEMBER 4
41 #define MR_STR_CONTACT 6
42 #define MR_STR_VOICEMESSAGE 7
43 #define MR_STR_DEADDROP 8
44 #define MR_STR_IMAGE 9
45 #define MR_STR_VIDEO 10
46 #define MR_STR_AUDIO 11
47 #define MR_STR_FILE 12
48 #define MR_STR_STATUSLINE 13
49 #define MR_STR_NEWGROUPDRAFT 14
50 #define MR_STR_MSGGRPNAME 15
51 #define MR_STR_MSGGRPIMGCHANGED 16
52 #define MR_STR_MSGADDMEMBER 17
53 #define MR_STR_MSGDELMEMBER 18
54 #define MR_STR_MSGGROUPLEFT 19
55 #define MR_STR_ERROR 20
56 #define MR_STR_SELFNOTINGRP 21
57 #define MR_STR_NONETWORK 22
58 #define MR_STR_GIF 23
59 #define MR_STR_ENCRYPTEDMSG 24
60 #define MR_STR_ENCR_E2E 25
61 #define MR_STR_ENCR_TRANSP 27
62 #define MR_STR_ENCR_NONE 28
63 #define MR_STR_FINGERPRINTS 30
64 #define MR_STR_READRCPT 31
65 #define MR_STR_READRCPT_MAILBODY 32
66 #define MR_STR_MSGGRPIMGDELETED 33
67 #define MR_STR_E2E_FINE 34
68 #define MR_STR_E2E_NO_AUTOCRYPT 35
69 #define MR_STR_E2E_DIS_BY_YOU 36
70 #define MR_STR_E2E_DIS_BY_RCPT 37
71 #define MR_STR_ARCHIVEDCHATS 40
72 #define MR_STR_STARREDMSGS 41
73 
74 
75 /* should be set up by mrmailbox_new() */
76 extern mrmailbox_t* s_localize_mb_obj;
77 
78 
79 /* Return the string with the given ID by calling MR_EVENT_GET_STRING.
80 The result must be free()'d! */
81 char* mrstock_str (int id);
82 
83 
84 /* Replaces the first `%1$s` in the given String-ID by the given value.
85 The result must be free()'d! */
86 char* mrstock_str_repl_string (int id, const char* value);
87 char* mrstock_str_repl_int (int id, int value);
88 
89 
90 /* Replaces the first `%1$s` and `%2$s` in the given String-ID by the two given strings.
91 The result must be free()'d! */
92 char* mrstock_str_repl_string2 (int id, const char*, const char*);
93 
94 
95 /* Return a string with a correct plural form by callint MR_EVENT_GET_QUANTITY_STRING.
96 The result must be free()'d! */
97 char* mrstock_str_repl_pl (int id, int cnt);
98 
99 
100 #ifdef __cplusplus
101 } /* /extern "C" */
102 #endif
103 #endif /* __MRSTOCK_H__ */
104 
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #ifndef __MRSTOCK_H__
24 #define __MRSTOCK_H__
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
29 
30 #include "mrmailbox.h"
31 #include <stdlib.h>
32 #include <string.h>
33 
34 
35 /* Strings requested by MR_EVENT_GET_STRING and MR_EVENT_GET_QUANTITY_STRING */
36 #define MR_STR_FREE_ 0
37 #define MR_STR_NOMESSAGES 1
38 #define MR_STR_SELF 2
39 #define MR_STR_DRAFT 3
40 #define MR_STR_MEMBER 4
41 #define MR_STR_CONTACT 6
42 #define MR_STR_VOICEMESSAGE 7
43 #define MR_STR_DEADDROP 8
44 #define MR_STR_IMAGE 9
45 #define MR_STR_VIDEO 10
46 #define MR_STR_AUDIO 11
47 #define MR_STR_FILE 12
48 #define MR_STR_STATUSLINE 13
49 #define MR_STR_NEWGROUPDRAFT 14
50 #define MR_STR_MSGGRPNAME 15
51 #define MR_STR_MSGGRPIMGCHANGED 16
52 #define MR_STR_MSGADDMEMBER 17
53 #define MR_STR_MSGDELMEMBER 18
54 #define MR_STR_MSGGROUPLEFT 19
55 #define MR_STR_ERROR 20
56 #define MR_STR_SELFNOTINGRP 21
57 #define MR_STR_NONETWORK 22
58 #define MR_STR_GIF 23
59 #define MR_STR_ENCRYPTEDMSG 24
60 #define MR_STR_ENCR_E2E 25
61 #define MR_STR_ENCR_TRANSP 27
62 #define MR_STR_ENCR_NONE 28
63 #define MR_STR_FINGERPRINTS 30
64 #define MR_STR_READRCPT 31
65 #define MR_STR_READRCPT_MAILBODY 32
66 #define MR_STR_MSGGRPIMGDELETED 33
67 #define MR_STR_E2E_FINE 34
68 #define MR_STR_E2E_NO_AUTOCRYPT 35
69 #define MR_STR_E2E_DIS_BY_YOU 36
70 #define MR_STR_E2E_DIS_BY_RCPT 37
71 #define MR_STR_ARCHIVEDCHATS 40
72 #define MR_STR_STARREDMSGS 41
73 
74 
75 /* should be set up by mrmailbox_new() */
76 extern mrmailbox_t* s_localize_mb_obj;
77 
78 
79 /* Return the string with the given ID by calling MR_EVENT_GET_STRING.
80 The result must be free()'d! */
81 char* mrstock_str (int id);
82 
83 
84 /* Replaces the first `%1$s` in the given String-ID by the given value.
85 The result must be free()'d! */
86 char* mrstock_str_repl_string (int id, const char* value);
87 char* mrstock_str_repl_int (int id, int value);
88 
89 
90 /* Replaces the first `%1$s` and `%2$s` in the given String-ID by the two given strings.
91 The result must be free()'d! */
92 char* mrstock_str_repl_string2 (int id, const char*, const char*);
93 
94 
95 /* Return a string with a correct plural form by callint MR_EVENT_GET_QUANTITY_STRING.
96 The result must be free()'d! */
97 char* mrstock_str_repl_pl (int id, int cnt);
98 
99 
100 #ifdef __cplusplus
101 } /* /extern "C" */
102 #endif
103 #endif /* __MRSTOCK_H__ */
104 
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/mrtools_8h_source.html b/docs/html/mrtools_8h_source.html similarity index 99% rename from docs/user/html/mrtools_8h_source.html rename to docs/html/mrtools_8h_source.html index 5786c032..f5cc38d3 100644 --- a/docs/user/html/mrtools_8h_source.html +++ b/docs/html/mrtools_8h_source.html @@ -88,11 +88,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
mrtools.h
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Some tools and enhancements to the used libraries, there should be
24 no references to mrmailbox_t and other "larger" classes here. */
25 
26 
27 #ifndef __MRTOOLS_H__
28 #define __MRTOOLS_H__
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32 
33 
34 /*** library-private **********************************************************/
35 
36 /* match tools */
37 int mr_exactly_one_bit_set (int v);
38 
39 /* string tools */
40 char* safe_strdup (const char*); /* safe_strdup() returns empty string if NULL is given, never returns NULL (exists on errors) */
41 char* strdup_keep_null (const char*); /* strdup(NULL) is undefined, safe_strdup_keep_null(NULL) returns NULL in this case */
42 int atoi_null_is_0 (const char*);
43 void mr_ltrim (char*);
44 void mr_rtrim (char*);
45 void mr_trim (char*);
46 char* mr_strlower (const char*); /* the result must be free()'d */
47 void mr_strlower_in_place (char*);
48 int mr_str_replace (char** haystack, const char* needle, const char* replacement); /* haystack may be realloc()'d, returns the number of replacements */
49 char* mr_null_terminate (const char*, int bytes); /* the result must be free()'d */
50 char* mr_mprintf (const char* format, ...); /* The result must be free()'d. */
51 void mr_remove_cr_chars (char*); /* remove all \r characters from string */
52 void mr_replace_bad_utf8_chars (char*); /* replace bad UTF-8 characters by sequences of `_` (to avoid problems in filenames, we do not use eg. `?`) the function is useful if strings are unexpectingly encoded eg. as ISO-8859-1 */
53 void mr_truncate_n_unwrap_str (char*, int approx_characters, int do_unwrap);
54 carray* mr_split_into_lines (const char* buf_terminated); /* split string into lines*/
55 void mr_free_splitted_lines (carray* lines);
56 char* mr_insert_breaks (const char*, int break_every, const char* break_chars); /* insert a break every n characters, the return must be free()'d */
57 char* mr_arr_to_string (const uint32_t*, int cnt);
58 char* mr_decode_header_string (const char*); /* the result must be free()'d */
59 char* mr_encode_header_string (const char*); /* the result must be free()'d */
60 char* imap_modified_utf7_to_utf8 (const char *mbox, int change_spaces);
61 char* imap_utf8_to_modified_utf7 (const char *src, int change_spaces);
62 char* mr_url_encode (const char*); /* the result must be free()'d */
63 char* encode_base64 (const char * in, int len); /* prototype, from libetpan/src/data-types/base64.h which cannot be included without adding libetpan/src/... to the include-search-paths, which would result in double-file-name-errors */
64 
65 /* string builder */
66 typedef struct mrstrbuilder_t
67 {
68  char* m_buf;
69  int m_allocated;
70  int m_free;
71  char* m_eos;
72 } mrstrbuilder_t;
73 void mrstrbuilder_init (mrstrbuilder_t* ths);
74 char* mrstrbuilder_cat (mrstrbuilder_t* ths, const char* text);
75 void mrstrbuilder_empty(mrstrbuilder_t* ths); /* set the string to a lenght of 0, does not free the buffer */
76 
77 
78 /* carray/clist tools */
79 int carray_search (carray*, void* needle, unsigned int* indx); /* returns 1/0 and the index if `indx` is not NULL */
80 void clist_free_content (const clist*); /* calls free() for each item content */
81 int clist_search_string_nocase (const clist*, const char* str);
82 
83 /* date/time tools */
84 #define MR_INVALID_TIMESTAMP (-1)
85 time_t mr_timestamp_from_date (struct mailimf_date_time * date_time); /* the result is UTC or MR_INVALID_TIMESTAMP */
86 char* mr_timestamp_to_str (time_t); /* the return value must be free()'d */
87 struct mailimap_date_time* mr_timestamp_to_mailimap_date_time (time_t);
88 long mr_gm2local_offset (void);
89 
90 /* timesmearing */
91 time_t mr_smeared_time__ (void);
92 time_t mr_create_smeared_timestamp__ (void);
93 time_t mr_create_smeared_timestamps__(int count);
94 
95 /* Message-ID tools */
96 #define MR_VALID_ID_LEN 11
97 char* mr_create_id (void);
98 char* mr_create_dummy_references_mid (void);
99 char* mr_create_incoming_rfc724_mid (time_t message_timestamp, uint32_t contact_id_from, carray* contact_ids_to);
100 char* mr_create_outgoing_rfc724_mid (const char* grpid, const char* addr);
101 char* mr_extract_grpid_from_rfc724_mid (const char* rfc724_mid);
102 char* mr_extract_grpid_from_rfc724_mid_list(const clist* rfc724_mid_list);
103 
104 
105 /* file tools */
106 int mr_file_exist (const char* pathNfilename);
107 size_t mr_get_filebytes (const char* pathNfilename);
108 char* mr_get_filename (const char* pathNfilename); /* the return value must be free()'d */
109 int mr_delete_file (const char* pathNFilename, mrmailbox_t* log);
110 int mr_copy_file (const char* src_pathNFilename, const char* dest_pathNFilename, mrmailbox_t* log);
111 int mr_create_folder (const char* pathNfilename, mrmailbox_t* log);
112 int mr_write_file (const char* pathNfilename, const void* buf, size_t buf_bytes, mrmailbox_t* log);
113 int mr_read_file (const char* pathNfilename, void** buf, size_t* buf_bytes, mrmailbox_t* log);
114 char* mr_get_filesuffix_lc (const char* pathNfilename); /* the returned suffix is lower-case */
115 void mr_split_filename (const char* pathNfilename, char** ret_basename, char** ret_all_suffixes_incl_dot); /* the case of the suffix is preserved! */
116 int mr_get_filemeta (const void* buf, size_t buf_bytes, uint32_t* ret_width, uint32_t *ret_height);
117 char* mr_get_fine_pathNfilename (const char* folder, const char* desired_name);
118 
119 /* macros */
120 #define MR_QUOTEHELPER(name) #name
121 #define MR_STRINGIFY(macro) MR_QUOTEHELPER(macro)
122 #define MR_MIN(X, Y) (((X) < (Y))? (X) : (Y))
123 #define MR_MAX(X, Y) (((X) > (Y))? (X) : (Y))
124 
125 
126 #ifdef __cplusplus
127 } /* /extern "C" */
128 #endif
129 #endif /* __MRTOOLS_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
+
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Some tools and enhancements to the used libraries, there should be
24 no references to mrmailbox_t and other "larger" classes here. */
25 
26 
27 #ifndef __MRTOOLS_H__
28 #define __MRTOOLS_H__
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32 
33 
34 /*** library-private **********************************************************/
35 
36 /* match tools */
37 int mr_exactly_one_bit_set (int v);
38 
39 /* string tools */
40 char* safe_strdup (const char*); /* safe_strdup() returns empty string if NULL is given, never returns NULL (exists on errors) */
41 char* strdup_keep_null (const char*); /* strdup(NULL) is undefined, safe_strdup_keep_null(NULL) returns NULL in this case */
42 int atoi_null_is_0 (const char*);
43 void mr_ltrim (char*);
44 void mr_rtrim (char*);
45 void mr_trim (char*);
46 char* mr_strlower (const char*); /* the result must be free()'d */
47 void mr_strlower_in_place (char*);
48 int mr_str_replace (char** haystack, const char* needle, const char* replacement); /* haystack may be realloc()'d, returns the number of replacements */
49 char* mr_null_terminate (const char*, int bytes); /* the result must be free()'d */
50 char* mr_mprintf (const char* format, ...); /* The result must be free()'d. */
51 void mr_remove_cr_chars (char*); /* remove all \r characters from string */
52 void mr_replace_bad_utf8_chars (char*); /* replace bad UTF-8 characters by sequences of `_` (to avoid problems in filenames, we do not use eg. `?`) the function is useful if strings are unexpectingly encoded eg. as ISO-8859-1 */
53 void mr_truncate_n_unwrap_str (char*, int approx_characters, int do_unwrap);
54 carray* mr_split_into_lines (const char* buf_terminated); /* split string into lines*/
55 void mr_free_splitted_lines (carray* lines);
56 char* mr_insert_breaks (const char*, int break_every, const char* break_chars); /* insert a break every n characters, the return must be free()'d */
57 char* mr_arr_to_string (const uint32_t*, int cnt);
58 char* mr_decode_header_string (const char*); /* the result must be free()'d */
59 char* mr_encode_header_string (const char*); /* the result must be free()'d */
60 char* imap_modified_utf7_to_utf8 (const char *mbox, int change_spaces);
61 char* imap_utf8_to_modified_utf7 (const char *src, int change_spaces);
62 char* mr_url_encode (const char*); /* the result must be free()'d */
63 char* encode_base64 (const char * in, int len); /* prototype, from libetpan/src/data-types/base64.h which cannot be included without adding libetpan/src/... to the include-search-paths, which would result in double-file-name-errors */
64 
65 /* string builder */
66 typedef struct mrstrbuilder_t
67 {
68  char* m_buf;
69  int m_allocated;
70  int m_free;
71  char* m_eos;
72 } mrstrbuilder_t;
73 void mrstrbuilder_init (mrstrbuilder_t* ths);
74 char* mrstrbuilder_cat (mrstrbuilder_t* ths, const char* text);
75 void mrstrbuilder_empty(mrstrbuilder_t* ths); /* set the string to a lenght of 0, does not free the buffer */
76 
77 
78 /* carray/clist tools */
79 int carray_search (carray*, void* needle, unsigned int* indx); /* returns 1/0 and the index if `indx` is not NULL */
80 void clist_free_content (const clist*); /* calls free() for each item content */
81 int clist_search_string_nocase (const clist*, const char* str);
82 
83 /* date/time tools */
84 #define MR_INVALID_TIMESTAMP (-1)
85 time_t mr_timestamp_from_date (struct mailimf_date_time * date_time); /* the result is UTC or MR_INVALID_TIMESTAMP */
86 char* mr_timestamp_to_str (time_t); /* the return value must be free()'d */
87 struct mailimap_date_time* mr_timestamp_to_mailimap_date_time (time_t);
88 long mr_gm2local_offset (void);
89 
90 /* timesmearing */
91 time_t mr_smeared_time__ (void);
92 time_t mr_create_smeared_timestamp__ (void);
93 time_t mr_create_smeared_timestamps__(int count);
94 
95 /* Message-ID tools */
96 #define MR_VALID_ID_LEN 11
97 char* mr_create_id (void);
98 char* mr_create_dummy_references_mid (void);
99 char* mr_create_incoming_rfc724_mid (time_t message_timestamp, uint32_t contact_id_from, carray* contact_ids_to);
100 char* mr_create_outgoing_rfc724_mid (const char* grpid, const char* addr);
101 char* mr_extract_grpid_from_rfc724_mid (const char* rfc724_mid);
102 char* mr_extract_grpid_from_rfc724_mid_list(const clist* rfc724_mid_list);
103 
104 
105 /* file tools */
106 int mr_file_exist (const char* pathNfilename);
107 size_t mr_get_filebytes (const char* pathNfilename);
108 char* mr_get_filename (const char* pathNfilename); /* the return value must be free()'d */
109 int mr_delete_file (const char* pathNFilename, mrmailbox_t* log);
110 int mr_copy_file (const char* src_pathNFilename, const char* dest_pathNFilename, mrmailbox_t* log);
111 int mr_create_folder (const char* pathNfilename, mrmailbox_t* log);
112 int mr_write_file (const char* pathNfilename, const void* buf, size_t buf_bytes, mrmailbox_t* log);
113 int mr_read_file (const char* pathNfilename, void** buf, size_t* buf_bytes, mrmailbox_t* log);
114 char* mr_get_filesuffix_lc (const char* pathNfilename); /* the returned suffix is lower-case */
115 void mr_split_filename (const char* pathNfilename, char** ret_basename, char** ret_all_suffixes_incl_dot); /* the case of the suffix is preserved! */
116 int mr_get_filemeta (const void* buf, size_t buf_bytes, uint32_t* ret_width, uint32_t *ret_height);
117 char* mr_get_fine_pathNfilename (const char* folder, const char* desired_name);
118 
119 /* macros */
120 #define MR_QUOTEHELPER(name) #name
121 #define MR_STRINGIFY(macro) MR_QUOTEHELPER(macro)
122 #define MR_MIN(X, Y) (((X) < (Y))? (X) : (Y))
123 #define MR_MAX(X, Y) (((X) > (Y))? (X) : (Y))
124 
125 
126 #ifdef __cplusplus
127 } /* /extern "C" */
128 #endif
129 #endif /* __MRTOOLS_H__ */
An object representing a single mailbox.
Definition: mrmailbox.h:141
diff --git a/docs/user/html/nav_f.png b/docs/html/nav_f.png similarity index 100% rename from docs/user/html/nav_f.png rename to docs/html/nav_f.png diff --git a/docs/user/html/nav_g.png b/docs/html/nav_g.png similarity index 100% rename from docs/user/html/nav_g.png rename to docs/html/nav_g.png diff --git a/docs/user/html/nav_h.png b/docs/html/nav_h.png similarity index 100% rename from docs/user/html/nav_h.png rename to docs/html/nav_h.png diff --git a/docs/user/html/open.png b/docs/html/open.png similarity index 100% rename from docs/user/html/open.png rename to docs/html/open.png diff --git a/docs/user/html/search/all_0.html b/docs/html/search/all_0.html similarity index 100% rename from docs/user/html/search/all_0.html rename to docs/html/search/all_0.html diff --git a/docs/user/html/search/all_0.js b/docs/html/search/all_0.js similarity index 100% rename from docs/user/html/search/all_0.js rename to docs/html/search/all_0.js diff --git a/docs/user/html/search/all_1.html b/docs/html/search/all_1.html similarity index 100% rename from docs/user/html/search/all_1.html rename to docs/html/search/all_1.html diff --git a/docs/user/html/search/all_1.js b/docs/html/search/all_1.js similarity index 100% rename from docs/user/html/search/all_1.js rename to docs/html/search/all_1.js diff --git a/docs/user/html/search/classes_0.html b/docs/html/search/classes_0.html similarity index 100% rename from docs/user/html/search/classes_0.html rename to docs/html/search/classes_0.html diff --git a/docs/user/html/search/classes_0.js b/docs/html/search/classes_0.js similarity index 100% rename from docs/user/html/search/classes_0.js rename to docs/html/search/classes_0.js diff --git a/docs/user/html/search/close.png b/docs/html/search/close.png similarity index 100% rename from docs/user/html/search/close.png rename to docs/html/search/close.png diff --git a/docs/user/html/search/functions_0.html b/docs/html/search/functions_0.html similarity index 100% rename from docs/user/html/search/functions_0.html rename to docs/html/search/functions_0.html diff --git a/docs/user/html/search/functions_0.js b/docs/html/search/functions_0.js similarity index 100% rename from docs/user/html/search/functions_0.js rename to docs/html/search/functions_0.js diff --git a/docs/user/html/search/mag_sel.png b/docs/html/search/mag_sel.png similarity index 100% rename from docs/user/html/search/mag_sel.png rename to docs/html/search/mag_sel.png diff --git a/docs/user/html/search/nomatches.html b/docs/html/search/nomatches.html similarity index 100% rename from docs/user/html/search/nomatches.html rename to docs/html/search/nomatches.html diff --git a/docs/user/html/search/pages_0.html b/docs/html/search/pages_0.html similarity index 100% rename from docs/user/html/search/pages_0.html rename to docs/html/search/pages_0.html diff --git a/docs/user/html/search/pages_0.js b/docs/html/search/pages_0.js similarity index 100% rename from docs/user/html/search/pages_0.js rename to docs/html/search/pages_0.js diff --git a/docs/user/html/search/related_0.html b/docs/html/search/related_0.html similarity index 100% rename from docs/user/html/search/related_0.html rename to docs/html/search/related_0.html diff --git a/docs/user/html/search/related_0.js b/docs/html/search/related_0.js similarity index 100% rename from docs/user/html/search/related_0.js rename to docs/html/search/related_0.js diff --git a/docs/user/html/search/search.css b/docs/html/search/search.css similarity index 100% rename from docs/user/html/search/search.css rename to docs/html/search/search.css diff --git a/docs/user/html/search/search.js b/docs/html/search/search.js similarity index 100% rename from docs/user/html/search/search.js rename to docs/html/search/search.js diff --git a/docs/user/html/search/search_l.png b/docs/html/search/search_l.png similarity index 100% rename from docs/user/html/search/search_l.png rename to docs/html/search/search_l.png diff --git a/docs/user/html/search/search_m.png b/docs/html/search/search_m.png similarity index 100% rename from docs/user/html/search/search_m.png rename to docs/html/search/search_m.png diff --git a/docs/user/html/search/search_r.png b/docs/html/search/search_r.png similarity index 100% rename from docs/user/html/search/search_r.png rename to docs/html/search/search_r.png diff --git a/docs/user/html/search/searchdata.js b/docs/html/search/searchdata.js similarity index 100% rename from docs/user/html/search/searchdata.js rename to docs/html/search/searchdata.js diff --git a/docs/user/html/search/typedefs_0.html b/docs/html/search/typedefs_0.html similarity index 100% rename from docs/user/html/search/typedefs_0.html rename to docs/html/search/typedefs_0.html diff --git a/docs/user/html/search/typedefs_0.js b/docs/html/search/typedefs_0.js similarity index 100% rename from docs/user/html/search/typedefs_0.js rename to docs/html/search/typedefs_0.js diff --git a/docs/user/html/search/variables_0.html b/docs/html/search/variables_0.html similarity index 100% rename from docs/user/html/search/variables_0.html rename to docs/html/search/variables_0.html diff --git a/docs/user/html/search/variables_0.js b/docs/html/search/variables_0.js similarity index 100% rename from docs/user/html/search/variables_0.js rename to docs/html/search/variables_0.js diff --git a/docs/user/html/splitbar.png b/docs/html/splitbar.png similarity index 100% rename from docs/user/html/splitbar.png rename to docs/html/splitbar.png diff --git a/docs/user/html/structmrchat__t-members.html b/docs/html/structmrchat__t-members.html similarity index 99% rename from docs/user/html/structmrchat__t-members.html rename to docs/html/structmrchat__t-members.html index 9d29458b..7df7d2e0 100644 --- a/docs/user/html/structmrchat__t-members.html +++ b/docs/html/structmrchat__t-members.html @@ -102,7 +102,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/docs/user/html/structmrchat__t.html b/docs/html/structmrchat__t.html similarity index 94% rename from docs/user/html/structmrchat__t.html rename to docs/html/structmrchat__t.html index 55ba7d1e..92e07f50 100644 --- a/docs/user/html/structmrchat__t.html +++ b/docs/html/structmrchat__t.html @@ -142,8 +142,6 @@ char * 

Detailed Description

An object representing a single chat in memory.

Chat objects are created using eg. mrmailbox_get_chat() and are not updated on database changes; if you want an update, you have to recreate the object.

- -

Definition at line 39 of file mrchat.h.

Member Function Documentation

@@ -168,8 +166,6 @@ char * 
Returns
None.
-

Definition at line 181 of file mrchat.c.

-
@@ -196,8 +192,6 @@ char * 
Returns
Subtitle as a string. Must be free()'d after usage.
-

Definition at line 215 of file mrchat.c.

- @@ -223,8 +217,6 @@ char * 
Returns
None.
-

Definition at line 160 of file mrchat.c.

-

Member Data Documentation

@@ -241,18 +233,16 @@ char * 59 of file mrchat.h.

-
The documentation for this struct was generated from the following files: diff --git a/docs/user/html/structmrchatlist__t-members.html b/docs/html/structmrchatlist__t-members.html similarity index 98% rename from docs/user/html/structmrchatlist__t-members.html rename to docs/html/structmrchatlist__t-members.html index 1e295e9a..6c748aa3 100644 --- a/docs/user/html/structmrchatlist__t-members.html +++ b/docs/html/structmrchatlist__t-members.html @@ -98,7 +98,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/structmrchatlist__t.html b/docs/html/structmrchatlist__t.html similarity index 93% rename from docs/user/html/structmrchatlist__t.html rename to docs/html/structmrchatlist__t.html index 9fa37d0d..1999057b 100644 --- a/docs/user/html/structmrchatlist__t.html +++ b/docs/html/structmrchatlist__t.html @@ -126,8 +126,6 @@ Public Attributes

Detailed Description

An object representing a single chatlist in memory.

Chatlist objects contain chat IDs and, if possible, message IDs belonging to them. Chatlist objects are created eg. using mrmailbox_get_chatlist(). The chatlist object is not updated. If you want an update, you have to recreate the object.

- -

Definition at line 42 of file mrchatlist.h.

Member Function Documentation

@@ -152,8 +150,6 @@ Public Attributes
Returns
None.
-

Definition at line 83 of file mrchatlist.c.

-
@@ -189,8 +185,6 @@ Public Attributes
Returns
Returns the chat_id of the item at the given index. Index must be between 0 and mrchatlist_get_cnt()-1.
-

Definition at line 121 of file mrchatlist.c.

- @@ -216,8 +210,6 @@ Public Attributes
Returns
Returns the number of items in a mrchatlist_t object. 0 on errors or if the list is empty.
-

Definition at line 101 of file mrchatlist.c.

- @@ -253,8 +245,6 @@ Public Attributes
Returns
Returns the message_id of the item at the given index. Index must be between 0 and mrchatlist_get_cnt()-1. If there is no message at the given index (eg. the chat may be empty), 0 is returned.
-

Definition at line 151 of file mrchatlist.c.

- @@ -306,8 +296,6 @@ Public Attributes
Returns
The result must be freed using mrpoortext_unref(). The function never returns NULL.
-

Definition at line 201 of file mrchatlist.c.

- @@ -334,18 +322,16 @@ Public Attributes
Returns
None.
-

Definition at line 62 of file mrchatlist.c.

-
The documentation for this struct was generated from the following files: diff --git a/docs/user/html/structmrcontact__t-members.html b/docs/html/structmrcontact__t-members.html similarity index 99% rename from docs/user/html/structmrcontact__t-members.html rename to docs/html/structmrcontact__t-members.html index 6007c179..dc74f38c 100644 --- a/docs/user/html/structmrcontact__t-members.html +++ b/docs/html/structmrcontact__t-members.html @@ -101,7 +101,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/structmrcontact__t.html b/docs/html/structmrcontact__t.html similarity index 93% rename from docs/user/html/structmrcontact__t.html rename to docs/html/structmrcontact__t.html index 98bf2e2c..3f158a5d 100644 --- a/docs/user/html/structmrcontact__t.html +++ b/docs/html/structmrcontact__t.html @@ -140,8 +140,6 @@ char * 

Detailed Description

An object representing a single contact in memory.

The contact object is not updated. If you want an update, you have to recreate the object.

- -

Definition at line 38 of file mrcontact.h.

Member Function Documentation

@@ -167,8 +165,6 @@ char * 
Returns
String with the first name, must be free()'d after usage.
-

Definition at line 99 of file mrcontact.c.

-
@@ -199,8 +195,6 @@ char * 
Returns
None. But the given buffer may be modified.
-

Definition at line 130 of file mrcontact.c.

-

Member Data Documentation

@@ -217,8 +211,6 @@ char * 46 of file mrcontact.h.

- @@ -234,18 +226,16 @@ char * 48 of file mrcontact.h.

-
The documentation for this struct was generated from the following files: diff --git a/docs/user/html/structmrmailbox__t-members.html b/docs/html/structmrmailbox__t-members.html similarity index 99% rename from docs/user/html/structmrmailbox__t-members.html rename to docs/html/structmrmailbox__t-members.html index 9dbc8357..049a6b3f 100644 --- a/docs/user/html/structmrmailbox__t-members.html +++ b/docs/html/structmrmailbox__t-members.html @@ -156,7 +156,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/structmrmailbox__t.html b/docs/html/structmrmailbox__t.html similarity index 93% rename from docs/user/html/structmrmailbox__t.html rename to docs/html/structmrmailbox__t.html index 9395cc7e..002d462e 100644 --- a/docs/user/html/structmrmailbox__t.html +++ b/docs/html/structmrmailbox__t.html @@ -304,8 +304,6 @@ void * 

Detailed Description

An object representing a single mailbox.

Each mailbox is linked to an IMAP/POP3 account and uses a separate SQLite database for offline functionality and for mailbox-related settings.

- -

Definition at line 141 of file mrmailbox.h.

Member Typedef Documentation

@@ -329,8 +327,6 @@ void * 
Returns
return 0 unless stated otherwise in the event parameter documentation
-

Definition at line 131 of file mrmailbox.h.

-

Member Function Documentation

@@ -369,8 +365,6 @@ void * 
Returns
The number of modified or added contacts.
-

Definition at line 4021 of file mrmailbox.c.

- @@ -415,8 +409,6 @@ void * 
Returns
1=member added to group, 0=error
-

Definition at line 3595 of file mrmailbox.c.

- @@ -462,8 +454,6 @@ void * 
Returns
None
-

Definition at line 2652 of file mrmailbox.c.

- @@ -508,8 +498,6 @@ void * 
Returns
None.
-

Definition at line 4290 of file mrmailbox.c.

- @@ -547,8 +535,6 @@ void * 
Returns
1=user is authorized, 0=user is not authorized.
-

Definition at line 1161 of file mrmailbox_imex.c.

- @@ -574,8 +560,6 @@ void * 
Returns
none
-

Definition at line 1075 of file mrmailbox.c.

- @@ -607,8 +591,6 @@ void * 
Returns
none
-

Definition at line 665 of file mrmailbox_configure.c.

- @@ -634,8 +616,6 @@ void * 
Returns
None
-

Definition at line 709 of file mrmailbox_configure.c.

- @@ -662,8 +642,6 @@ void * 
Returns
None
-

Definition at line 1578 of file mrmailbox.c.

- @@ -701,8 +679,6 @@ void * 
Returns
The created or reused chat ID on success. 0 on errors.
-

Definition at line 1816 of file mrmailbox.c.

- @@ -747,8 +723,6 @@ void * 
Returns
Contact ID of the created or reused contact.
-

Definition at line 3985 of file mrmailbox.c.

- @@ -787,8 +761,6 @@ void * 
Returns
The chat ID of the new group chat, 0 on errors.
-

Definition at line 3333 of file mrmailbox.c.

- @@ -816,8 +788,6 @@ void * 
Returns
Setup code, must be free()'d after usage.
-

Definition at line 1218 of file mrmailbox_imex.c.

- @@ -863,8 +833,6 @@ void * 
Returns
None
-

Definition at line 2773 of file mrmailbox.c.

- @@ -902,8 +870,6 @@ void * 
Returns
1=success, 0=error
-

Definition at line 4509 of file mrmailbox.c.

- @@ -948,8 +914,6 @@ void * 
Returns
none
-

Definition at line 5096 of file mrmailbox.c.

- @@ -975,8 +939,6 @@ void * 
Returns
None
-

Definition at line 1605 of file mrmailbox.c.

- @@ -1027,8 +989,6 @@ void * 
Returns
none
-

Definition at line 4868 of file mrmailbox.c.

- @@ -1054,8 +1014,6 @@ void * 
Returns
String which must be free()'d after usage.
-

Definition at line 1276 of file mrmailbox.c.

- @@ -1081,8 +1039,6 @@ void * 
Returns
An array containing all blocked contact IDs. Must be carray_free()'d after usage.
-

Definition at line 4140 of file mrmailbox.c.

- @@ -1107,8 +1063,6 @@ void * 4174 of file mrmailbox.c.

- @@ -1146,8 +1100,6 @@ void * 
Returns
A chat object, must be freed using mrchat_unref() when done.
-

Definition at line 1709 of file mrmailbox.c.

- @@ -1189,8 +1141,6 @@ void * 
Returns
an array of contact IDs belonging to the chat; must be freed using carray_free() when done.
-

Definition at line 2003 of file mrmailbox.c.

- @@ -1227,8 +1177,6 @@ void * 
Returns
If there is a normal chat with the given contact_id, this chat_id is returned. If there is no normal chat with the contact_id, the function returns 0.
-

Definition at line 1789 of file mrmailbox.c.

- @@ -1280,8 +1228,6 @@ void * 
Returns
An array with messages from the given chat ID that have the wanted message types.
-

Definition at line 1896 of file mrmailbox.c.

- @@ -1333,8 +1279,6 @@ void * 
Returns
Array of message IDs, must be carray_free()'d when no longer used.
-

Definition at line 2120 of file mrmailbox.c.

- @@ -1382,8 +1326,6 @@ void * 
Returns
A chatlist as an mrchatlist_t object. Must be freed using mrchatlist_unref() when no longer used
-

Definition at line 1663 of file mrmailbox.c.

- @@ -1428,8 +1370,6 @@ void * 
Returns
Returns current value of "key", if "key" is unset, "def" is returned (which may be NULL) If the returned values is not NULL, the return value must be free()'d,
-

Definition at line 1207 of file mrmailbox.c.

- @@ -1465,8 +1405,6 @@ void * mrmailbox_get_config() but gets the value as an integer instead of a string.

-

Definition at line 1251 of file mrmailbox.c.

- @@ -1504,8 +1442,6 @@ void * 
Returns
The contact object, must be freed using mrcontact_unref() when no longer used. NULL on errors.
-

Definition at line 4216 of file mrmailbox.c.

- @@ -1543,8 +1479,6 @@ void * 
Returns
multi-line text, must be free()'d after usage.
-

Definition at line 4381 of file mrmailbox.c.

- @@ -1582,8 +1516,6 @@ void * 
Returns
Number of fresh messages in the given chat. 0 for errors or if there are no fresh messages.
-

Definition at line 2616 of file mrmailbox.c.

- @@ -1609,8 +1541,6 @@ void * 2052 of file mrmailbox.c.

- @@ -1637,8 +1567,6 @@ void * 
Returns
String which must be free()'d after usage. Never returns NULL.
-

Definition at line 1292 of file mrmailbox.c.

- @@ -1676,8 +1604,6 @@ void * 
Returns
An array containing all contact IDs. Must be carray_free()'d after usage.
-

Definition at line 4076 of file mrmailbox.c.

- @@ -1715,8 +1641,6 @@ void * 
Returns
A mrmsg_t message object. When done, the object must be freed using mrmsg_unref()
-

Definition at line 4701 of file mrmailbox.c.

- @@ -1754,8 +1678,6 @@ void * 
Returns
text string, must be free()'d after usage
-

Definition at line 4743 of file mrmailbox.c.

- @@ -1800,8 +1722,6 @@ void * 
Returns
Returns the message ID that should be displayed next. The returned message is in the same chat as the given one. Typically, this result is passed again to mrmailbox_get_next_media() later on the next swipe.
-

Definition at line 1927 of file mrmailbox.c.

- @@ -1838,8 +1758,6 @@ void * 
Returns
Number of total messages in the given chat. 0 for errors or empty chats.
-

Definition at line 2588 of file mrmailbox.c.

- @@ -1859,8 +1777,6 @@ void * 
Returns
String with version number as major.minor.revision. The return value must be free()'d.
-

Definition at line 1490 of file mrmailbox.c.

- @@ -1912,8 +1828,6 @@ void * 
Returns
None.
-

Definition at line 1108 of file mrmailbox_imex.c.

- @@ -1951,8 +1865,6 @@ void * 
Returns
String with the backup file or NULL; returned strings must be free()'d.
-

Definition at line 849 of file mrmailbox_imex.c.

- @@ -1979,8 +1891,6 @@ void * 
Returns
None
-

Definition at line 735 of file mrmailbox_configure.c.

- @@ -2024,8 +1934,6 @@ void * 
Returns
1=contact ID is member of chat ID, 0=contact is not in chat
-

Definition at line 3565 of file mrmailbox.c.

- @@ -2051,8 +1959,6 @@ void * 
Returns
0=mailbox is not open, 1=mailbox is open.
-

Definition at line 1109 of file mrmailbox.c.

- @@ -2090,8 +1996,6 @@ void * 
Returns
None.
-

Definition at line 1755 of file mrmailbox.c.

- @@ -2129,8 +2033,6 @@ void * 
Returns
none
-

Definition at line 4264 of file mrmailbox.c.

- @@ -2175,8 +2077,6 @@ void * 
Returns
none
-

Definition at line 5237 of file mrmailbox.c.

- @@ -2227,8 +2127,6 @@ void * 
Returns
a mailbox object with some public members the object must be passed to the other mailbox functions and the object must be freed using mrmailbox_unref() after usage.
-

Definition at line 897 of file mrmailbox.c.

- @@ -2273,8 +2171,6 @@ void * 
Returns
1 on success, 0 on failure
-

Definition at line 1010 of file mrmailbox.c.

- @@ -2319,8 +2215,6 @@ void * 
Returns
1=member removed from group, 0=error
-

Definition at line 3678 of file mrmailbox.c.

- @@ -2366,8 +2260,6 @@ void * 
Returns
An array of message IDs. Must be freed using carray_free() when no longer needed. If nothing can be found, the function returns NULL.
-

Definition at line 2224 of file mrmailbox.c.

- @@ -2412,8 +2304,6 @@ void * 
Returns
The ID of the message that is about being sent.
-

Definition at line 3142 of file mrmailbox.c.

- @@ -2458,8 +2348,6 @@ void * 
Returns
The ID of the message that is about being sent.
-

Definition at line 3101 of file mrmailbox.c.

- @@ -2504,8 +2392,6 @@ void * 
Returns
1=success, 0=error
-

Definition at line 3476 of file mrmailbox.c.

- @@ -2550,8 +2436,6 @@ void * 
Returns
1=success, 0=error
-

Definition at line 3399 of file mrmailbox.c.

- @@ -2611,8 +2495,6 @@ void * 
Returns
0=failure, 1=success
-

Definition at line 1176 of file mrmailbox.c.

- @@ -2648,8 +2530,6 @@ void * mrmailbox_set_config() but sets an integer instead of a string. If there is already a key with a string set, this is overwritten by the given integer value.

-

Definition at line 1229 of file mrmailbox.c.

- @@ -2693,8 +2573,6 @@ void * 
Returns
None.
-

Definition at line 2371 of file mrmailbox.c.

- @@ -2746,8 +2624,6 @@ void * 
Returns
none
-

Definition at line 4954 of file mrmailbox.c.

- @@ -2774,8 +2650,6 @@ void * 
Returns
none
-

Definition at line 954 of file mrmailbox.c.

-

Member Data Documentation

@@ -2791,8 +2665,6 @@ void * 145 of file mrmailbox.h.

- @@ -2807,20 +2679,18 @@ void * 144 of file mrmailbox.h.

-
The documentation for this struct was generated from the following files: diff --git a/docs/user/html/structmrmsg__t-members.html b/docs/html/structmrmsg__t-members.html similarity index 99% rename from docs/user/html/structmrmsg__t-members.html rename to docs/html/structmrmsg__t-members.html index c4d37f46..5f643817 100644 --- a/docs/user/html/structmrmsg__t-members.html +++ b/docs/html/structmrmsg__t-members.html @@ -111,7 +111,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/structmrmsg__t.html b/docs/html/structmrmsg__t.html similarity index 91% rename from docs/user/html/structmrmsg__t.html rename to docs/html/structmrmsg__t.html index 2bef2101..23a3370a 100644 --- a/docs/user/html/structmrmsg__t.html +++ b/docs/html/structmrmsg__t.html @@ -166,8 +166,6 @@ char * 

Detailed Description

An object representing a single message in memory.

The message object is not updated. If you want an update, you have to recreate the object.

- -

Definition at line 40 of file mrmsg.h.

Member Function Documentation

@@ -192,8 +190,6 @@ char * 
Returns
None.
-

Definition at line 87 of file mrmsg.c.

-
@@ -221,8 +217,6 @@ char * 
Returns
poortext object that must be unref'd using mrpoortext_unref() when no longer used.
-

Definition at line 480 of file mrmsg.c.

- @@ -253,8 +247,6 @@ char * mrpoortext_unref(). Typically used to display a search result.

Returns
The returned summary is similar to mrchatlist_get_summary(), however, without "draft", "no messages" and so on.
-

Definition at line 263 of file mrmsg.c.

- @@ -284,8 +276,6 @@ char * 340 of file mrmsg.c.

- @@ -305,8 +295,6 @@ char * mrmailbox_send_msg(). Moreover, they are returned eg. from mrmailbox_get_msg(), set up with the current state of a message. The message object is not updated; to achieve this, you have to recreate it.

Returns
The created message object.
-

Definition at line 41 of file mrmsg.c.

- @@ -344,8 +332,6 @@ char * 
Returns
None.
-

Definition at line 125 of file mrmsg.c.

- @@ -371,8 +357,6 @@ char * 
Returns
1=padlock should be shown beside message, 0=do not show a padlock beside the message.
-

Definition at line 302 of file mrmsg.c.

- @@ -398,8 +382,6 @@ char * 66 of file mrmsg.c.

-

Member Data Documentation

@@ -416,8 +398,6 @@ char * 49 of file mrmsg.h.

- @@ -433,8 +413,6 @@ char * 47 of file mrmsg.h.

- @@ -449,8 +427,6 @@ char * 45 of file mrmsg.h.

- @@ -466,8 +442,6 @@ char * 75 of file mrmsg.h.

- @@ -483,8 +457,6 @@ char * 73 of file mrmsg.h.

- @@ -500,8 +472,6 @@ char * 74 of file mrmsg.h.

- @@ -516,8 +486,6 @@ char * 70 of file mrmsg.h.

- @@ -532,8 +500,6 @@ char * 50 of file mrmsg.h.

- @@ -549,8 +515,6 @@ char * 48 of file mrmsg.h.

- @@ -565,18 +529,16 @@ char * 60 of file mrmsg.h.

-
The documentation for this struct was generated from the following files: diff --git a/docs/user/html/structmrparam__t-members.html b/docs/html/structmrparam__t-members.html similarity index 98% rename from docs/user/html/structmrparam__t-members.html rename to docs/html/structmrparam__t-members.html index ec9d4303..76193da8 100644 --- a/docs/user/html/structmrparam__t-members.html +++ b/docs/html/structmrparam__t-members.html @@ -99,7 +99,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/structmrparam__t.html b/docs/html/structmrparam__t.html similarity index 92% rename from docs/user/html/structmrparam__t.html rename to docs/html/structmrparam__t.html index d445b15c..de5d1050 100644 --- a/docs/user/html/structmrparam__t.html +++ b/docs/html/structmrparam__t.html @@ -124,8 +124,6 @@ Public Member Functions

Detailed Description

An object for handling key=value parameter lists.

The parameter object is used eg. by mrchat_t or mrmsg_t. To access the single parameters use the setter and getter functions with an MRP_* constant.

- -

Definition at line 36 of file mrparam.h.

Member Function Documentation

@@ -150,8 +148,6 @@ Public Member Functions
Returns
None.
-

Definition at line 111 of file mrparam.c.

-
@@ -188,8 +184,6 @@ Public Member Functions
Returns
1=parameter exists in object, 0=parameter does not exist in parameter object.
-

Definition at line 161 of file mrparam.c.

- @@ -233,8 +227,6 @@ Public Member Functions
Returns
The stored value or the default value. In both cases, the returned value must be free()'d.
-

Definition at line 186 of file mrparam.c.

- @@ -278,8 +270,6 @@ Public Member Functions
Returns
The stored value or the default value.
-

Definition at line 223 of file mrparam.c.

- @@ -298,8 +288,6 @@ Public Member Functions

Create new parameter list object.

Returns
The created parameter list object.
-

Definition at line 69 of file mrparam.c.

- @@ -343,8 +331,6 @@ Public Member Functions
Returns
None.
-

Definition at line 253 of file mrparam.c.

- @@ -388,8 +374,6 @@ Public Member Functions
Returns
None.
-

Definition at line 318 of file mrparam.c.

- @@ -415,18 +399,16 @@ Public Member Functions -

Definition at line 90 of file mrparam.c.

-
The documentation for this struct was generated from the following files: diff --git a/docs/user/html/structmrpoortext__t-members.html b/docs/html/structmrpoortext__t-members.html similarity index 99% rename from docs/user/html/structmrpoortext__t-members.html rename to docs/html/structmrpoortext__t-members.html index 93c7af99..985c8774 100644 --- a/docs/user/html/structmrpoortext__t-members.html +++ b/docs/html/structmrpoortext__t-members.html @@ -101,7 +101,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); diff --git a/docs/user/html/structmrpoortext__t.html b/docs/html/structmrpoortext__t.html similarity index 95% rename from docs/user/html/structmrpoortext__t.html rename to docs/html/structmrpoortext__t.html index f6cc1a91..68acf628 100644 --- a/docs/user/html/structmrpoortext__t.html +++ b/docs/html/structmrpoortext__t.html @@ -135,8 +135,6 @@ int 

Detailed Description

the poortext object and some function accessing it.

A poortext object contains some strings together with their meaning and some attributes. The object is mainly used for summary returns of chats and chatlists

- -

Definition at line 35 of file mrpoortext.h.

Member Function Documentation

@@ -162,18 +160,16 @@ int 
Returns
None
-

Definition at line 55 of file mrpoortext.c.

-

The documentation for this struct was generated from the following files: diff --git a/docs/user/html/sync_off.png b/docs/html/sync_off.png similarity index 100% rename from docs/user/html/sync_off.png rename to docs/html/sync_off.png diff --git a/docs/user/html/sync_on.png b/docs/html/sync_on.png similarity index 100% rename from docs/user/html/sync_on.png rename to docs/html/sync_on.png diff --git a/docs/user/html/tab_a.png b/docs/html/tab_a.png similarity index 100% rename from docs/user/html/tab_a.png rename to docs/html/tab_a.png diff --git a/docs/user/html/tab_b.png b/docs/html/tab_b.png similarity index 100% rename from docs/user/html/tab_b.png rename to docs/html/tab_b.png diff --git a/docs/user/html/tab_h.png b/docs/html/tab_h.png similarity index 100% rename from docs/user/html/tab_h.png rename to docs/html/tab_h.png diff --git a/docs/user/html/tab_s.png b/docs/html/tab_s.png similarity index 100% rename from docs/user/html/tab_s.png rename to docs/html/tab_s.png diff --git a/docs/user/html/tabs.css b/docs/html/tabs.css similarity index 100% rename from docs/user/html/tabs.css rename to docs/html/tabs.css diff --git a/docs/user/html/user.css b/docs/html/user.css similarity index 100% rename from docs/user/html/user.css rename to docs/html/user.css diff --git a/docs/user/html/files.html b/docs/user/html/files.html deleted file mode 100644 index 71e007ed..00000000 --- a/docs/user/html/files.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - -Delta Chat Core C-Library: File List - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - -
- -
-
- - -
- -
- -
-
-
File List
-
-
-
Here is a list of all documented files with brief descriptions:
-
[detail level 12]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  src
 mraheader.c
 mraheader.h
 mrapeerstate.c
 mrapeerstate.h
 mrchat.c
 mrchat.h
 mrchatlist.c
 mrchatlist.h
 mrcontact.c
 mrcontact.h
 mrdehtml.c
 mrdehtml.h
 mrimap.c
 mrimap.h
 mrjob.c
 mrjob.h
 mrkey.c
 mrkey.h
 mrkeyring.c
 mrkeyring.h
 mrloginparam.c
 mrloginparam.h
 mrmailbox.c
 mrmailbox.h
 mrmailbox_configure.c
 mrmailbox_e2ee.c
 mrmailbox_imex.c
 mrmailbox_internal.h
 mrmailbox_log.c
 mrmailbox_tools.c
 mrmimefactory.c
 mrmimefactory.h
 mrmimeparser.c
 mrmimeparser.h
 mrmsg.c
 mrmsg.h
 mrosnative.c
 mrosnative.h
 mrparam.c
 mrparam.h
 mrpgp.c
 mrpgp.h
 mrpoortext.c
 mrpoortext.h
 mrsaxparser.c
 mrsaxparser.h
 mrsimplify.c
 mrsimplify.h
 mrsmtp.c
 mrsmtp.h
 mrsqlite3.c
 mrsqlite3.h
 mrstock.c
 mrstock.h
 mrtools.c
 mrtools.h
-
-
- - - - diff --git a/docs/user/html/mraheader_8c_source.html b/docs/user/html/mraheader_8c_source.html deleted file mode 100644 index ec466396..00000000 --- a/docs/user/html/mraheader_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mraheader.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mraheader.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <ctype.h>
24 #include "mrmailbox_internal.h"
25 #include "mraheader.h"
26 #include "mrapeerstate.h"
27 #include "mrmimeparser.h"
28 
29 
33 void mraheader_empty(mraheader_t* ths)
34 {
35  if( ths == NULL ) {
36  return;
37  }
38 
39  ths->m_prefer_encrypt = 0;
40 
41  free(ths->m_addr);
42  ths->m_addr = NULL;
43 
44  if( ths->m_public_key->m_binary ) {
45  mrkey_unref(ths->m_public_key);
46  ths->m_public_key = mrkey_new();
47  }
48 }
49 
50 
51 /*******************************************************************************
52  * Render Autocrypt Header
53  ******************************************************************************/
54 
55 
59 char* mraheader_render(const mraheader_t* ths)
60 {
61  int success = 0;
62  char* keybase64_wrapped = NULL;
63  mrstrbuilder_t ret;
64  mrstrbuilder_init(&ret);
65 
66  if( ths==NULL || ths->m_addr==NULL || ths->m_public_key->m_binary==NULL || ths->m_public_key->m_type!=MR_PUBLIC ) {
67  goto cleanup;
68  }
69 
70  mrstrbuilder_cat(&ret, "addr=");
71  mrstrbuilder_cat(&ret, ths->m_addr);
72  mrstrbuilder_cat(&ret, "; ");
73 
74  if( ths->m_prefer_encrypt==MRA_PE_MUTUAL ) {
75  mrstrbuilder_cat(&ret, "prefer-encrypt=mutual; ");
76  }
77 
78  mrstrbuilder_cat(&ret, "keydata= "); /* the trailing space together with mr_insert_breaks() allows a proper transport */
79 
80  /* adds a whitespace every 78 characters, this allows libEtPan to wrap the lines according to RFC 5322
81  (which may insert a linebreak before every whitespace) */
82  if( (keybase64_wrapped = mrkey_render_base64(ths->m_public_key, 78, " ", 0/*no checksum*/)) == NULL ) {
83  goto cleanup;
84  }
85 
86  mrstrbuilder_cat(&ret, keybase64_wrapped);
87 
88  success = 1;
89 
90 cleanup:
91  if( !success ) { free(ret.m_buf); ret.m_buf = NULL; }
92  free(keybase64_wrapped);
93  return ret.m_buf; /* NULL on errors, this may happen for various reasons */
94 }
95 
96 
97 /*******************************************************************************
98  * Parse Autocrypt Header
99  ******************************************************************************/
100 
101 
102 static int add_attribute(mraheader_t* ths, const char* name, const char* value /*may be NULL*/)
103 {
104  /* returns 0 if the attribute will result in an invalid header, 1 if the attribute is okay */
105  if( strcasecmp(name, "addr")==0 )
106  {
107  if( value == NULL
108  || strlen(value) < 3 || strchr(value, '@')==NULL || strchr(value, '.')==NULL /* rough check if email-address is valid */
109  || ths->m_addr /* email already given */ ) {
110  return 0;
111  }
112  ths->m_addr = mr_normalize_addr(value);
113  return 1;
114  }
115  #if 0 /* autoctypt 11/2017 no longer uses the type attribute and it will make the autocrypt header invalid */
116  else if( strcasecmp(name, "type")==0 )
117  {
118  if( value == NULL ) {
119  return 0; /* attribute with no value results in an invalid header */
120  }
121  if( strcasecmp(value, "1")==0 || strcasecmp(value, "0" /*deprecated*/)==0 || strcasecmp(value, "p" /*deprecated*/)==0 ) {
122  return 1; /* PGP-type */
123  }
124  return 0; /* unknown types result in an invalid header */
125  }
126  #endif
127  else if( strcasecmp(name, "prefer-encrypt")==0 )
128  {
129  if( value && strcasecmp(value, "mutual")==0 ) {
130  ths->m_prefer_encrypt = MRA_PE_MUTUAL;
131  return 1;
132  }
133  return 1; /* An Autocrypt level 0 client that sees the attribute with any other value (or that does not see the attribute at all) should interpret the value as nopreference.*/
134  }
135  else if( strcasecmp(name, "keydata")==0 )
136  {
137  if( value == NULL
138  || ths->m_public_key->m_binary || ths->m_public_key->m_bytes ) {
139  return 0; /* there is already a k*/
140  }
141  return mrkey_set_from_base64(ths->m_public_key, value, MR_PUBLIC);
142  }
143  else if( name[0]=='_' )
144  {
145  /* Autocrypt-Level0: unknown attributes starting with an underscore can be safely ignored */
146  return 1;
147  }
148 
149  /* Autocrypt-Level0: unknown attribute, treat the header as invalid */
150  return 0;
151 }
152 
153 
157 int mraheader_set_from_string(mraheader_t* ths, const char* header_str__)
158 {
159  /* according to RFC 5322 (Internet Message Format), the given string may contain `\r\n` before any whitespace.
160  we can ignore this issue as
161  (a) no key or value is expected to contain spaces,
162  (b) for the key, non-base64-characters are ignored and
163  (c) for parsing, we ignore `\r\n` as well as tabs for spaces */
164  #define AHEADER_WS "\t\r\n "
165  char *header_str = NULL;
166  char *p, *beg_attr_name, *after_attr_name, *beg_attr_value;
167  int success = 0;
168 
169  mraheader_empty(ths);
170  ths->m_prefer_encrypt = MRA_PE_NOPREFERENCE; /* value to use if the prefer-encrypted header is missing */
171 
172  if( ths == NULL || header_str__ == NULL ) {
173  goto cleanup;
174  }
175 
176  header_str = safe_strdup(header_str__);
177  p = header_str;
178  while( *p )
179  {
180  p += strspn(p, AHEADER_WS "=;"); /* forward to first attribute name beginning */
181  beg_attr_name = p;
182  beg_attr_value = NULL;
183  p += strcspn(p, AHEADER_WS "=;"); /* get end of attribute name (an attribute may have no value) */
184  if( p != beg_attr_name )
185  {
186  /* attribute found */
187  after_attr_name = p;
188  p += strspn(p, AHEADER_WS); /* skip whitespace between attribute name and possible `=` */
189  if( *p == '=' )
190  {
191  p += strspn(p, AHEADER_WS "="); /* skip spaces and equal signs */
192 
193  /* read unquoted attribute value until the first semicolon */
194  beg_attr_value = p;
195  p += strcspn(p, ";");
196  if( *p != '\0' ) {
197  *p = '\0';
198  p++;
199  }
200  mr_trim(beg_attr_value);
201  }
202  else
203  {
204  p += strspn(p, AHEADER_WS ";");
205  }
206  *after_attr_name = '\0';
207  if( !add_attribute(ths, beg_attr_name, beg_attr_value) ) {
208  goto cleanup; /* a bad attribute makes the whole header invalid */
209  }
210  }
211  }
212 
213  /* all needed data found? */
214  if( ths->m_addr && ths->m_public_key->m_binary ) {
215  success = 1;
216  }
217 
218 cleanup:
219  free(header_str);
220  if( !success ) { mraheader_empty(ths); }
221  return success;
222 }
223 
224 
225 /*******************************************************************************
226  * Main interface
227  ******************************************************************************/
228 
229 
233 mraheader_t* mraheader_new()
234 {
235  mraheader_t* ths = NULL;
236 
237  if( (ths=calloc(1, sizeof(mraheader_t)))==NULL ) {
238  exit(37); /* cannot allocate little memory, unrecoverable error */
239  }
240 
241  ths->m_public_key = mrkey_new();
242 
243  return ths;
244 }
245 
246 
250 void mraheader_unref(mraheader_t* ths)
251 {
252  if( ths==NULL ) {
253  return;
254  }
255 
256  free(ths->m_addr);
257  mrkey_unref(ths->m_public_key);
258  free(ths);
259 }
260 
261 
265 mraheader_t* mraheader_new_from_imffields(const char* wanted_from, const struct mailimf_fields* header)
266 {
267  clistiter* cur;
268  mraheader_t* fine_header = NULL;
269 
270  if( wanted_from == NULL || header == NULL ) {
271  return 0;
272  }
273 
274  for( cur = clist_begin(header->fld_list); cur!=NULL ; cur=clist_next(cur) )
275  {
276  struct mailimf_field* field = (struct mailimf_field*)clist_content(cur);
277  if( field && field->fld_type == MAILIMF_FIELD_OPTIONAL_FIELD )
278  {
279  struct mailimf_optional_field* optional_field = field->fld_data.fld_optional_field;
280  if( optional_field && optional_field->fld_name && strcasecmp(optional_field->fld_name, "Autocrypt")==0 )
281  {
282  /* header found, check if it is valid and matched the wanted address */
283  mraheader_t* test = mraheader_new();
284  if( !mraheader_set_from_string(test, optional_field->fld_value)
285  || strcasecmp(test->m_addr, wanted_from)!=0 ) {
286  mraheader_unref(test);
287  test = NULL;
288  }
289 
290  if( fine_header == NULL ) {
291  fine_header = test; /* may still be NULL */
292  }
293  else if( test ) {
294  mraheader_unref(fine_header);
295  mraheader_unref(test);
296  return NULL; /* more than one valid header for the same address results in an error, see Autocrypt Level 1 */
297  }
298  }
299  }
300  }
301 
302  return fine_header; /* may be NULL */
303 }
304 
- - - - diff --git a/docs/user/html/mrapeerstate_8c_source.html b/docs/user/html/mrapeerstate_8c_source.html deleted file mode 100644 index b79adc1a..00000000 --- a/docs/user/html/mrapeerstate_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrapeerstate.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrapeerstate.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrapeerstate.h"
25 #include "mraheader.h"
26 
27 
28 /*******************************************************************************
29  * mrapeerstate_t represents the state of an Autocrypt peer - Load/save
30  ******************************************************************************/
31 
32 
33 static void mrapeerstate_empty(mrapeerstate_t* ths)
34 {
35  if( ths == NULL ) {
36  return;
37  }
38 
39  ths->m_last_seen = 0;
40  ths->m_last_seen_autocrypt = 0;
41  ths->m_prefer_encrypt = 0;
42  ths->m_to_save = 0;
43 
44  free(ths->m_addr);
45  ths->m_addr = NULL;
46 
47  if( ths->m_public_key->m_binary ) {
48  mrkey_unref(ths->m_public_key);
49  ths->m_public_key = mrkey_new();
50  }
51 }
52 
53 
54 int mrapeerstate_load_from_db__(mrapeerstate_t* ths, mrsqlite3_t* sql, const char* addr)
55 {
56  int success = 0;
57  sqlite3_stmt* stmt;
58 
59  if( ths==NULL || sql == NULL || addr == NULL ) {
60  return 0;
61  }
62 
63  mrapeerstate_empty(ths);
64 
65  stmt = mrsqlite3_predefine__(sql, SELECT_aclpp_FROM_acpeerstates_WHERE_a,
66  "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key FROM acpeerstates WHERE addr=? COLLATE NOCASE;");
67  sqlite3_bind_text(stmt, 1, addr, -1, SQLITE_STATIC);
68  if( sqlite3_step(stmt) != SQLITE_ROW ) {
69  goto cleanup;
70  }
71  ths->m_addr = safe_strdup((char*)sqlite3_column_text (stmt, 0));
72  ths->m_last_seen = sqlite3_column_int64 (stmt, 1);
73  ths->m_last_seen_autocrypt = sqlite3_column_int64 (stmt, 2);
74  ths->m_prefer_encrypt = sqlite3_column_int (stmt, 3);
75  mrkey_set_from_stmt (ths->m_public_key, stmt, 4, MR_PUBLIC);
76 
77  success = 1;
78 
79 cleanup:
80  return success;
81 }
82 
83 
84 int mrapeerstate_save_to_db__(const mrapeerstate_t* ths, mrsqlite3_t* sql, int create)
85 {
86  int success = 0;
87  sqlite3_stmt* stmt;
88 
89  if( ths==NULL || sql==NULL
90  || ths->m_addr==NULL || ths->m_public_key->m_binary==NULL || ths->m_public_key->m_bytes<=0 ) {
91  return 0;
92  }
93 
94  if( create ) {
95  stmt = mrsqlite3_predefine__(sql, INSERT_INTO_acpeerstates_a, "INSERT INTO acpeerstates (addr) VALUES(?);");
96  sqlite3_bind_text(stmt, 1, ths->m_addr, -1, SQLITE_STATIC);
97  sqlite3_step(stmt);
98  }
99 
100  if( (ths->m_to_save&MRA_SAVE_ALL) || create )
101  {
102  stmt = mrsqlite3_predefine__(sql, UPDATE_acpeerstates_SET_lcpp_WHERE_a,
103  "UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, public_key=? WHERE addr=?;");
104  sqlite3_bind_int64(stmt, 1, ths->m_last_seen);
105  sqlite3_bind_int64(stmt, 2, ths->m_last_seen_autocrypt);
106  sqlite3_bind_int64(stmt, 3, ths->m_prefer_encrypt);
107  sqlite3_bind_blob (stmt, 4, ths->m_public_key->m_binary, ths->m_public_key->m_bytes, SQLITE_STATIC);
108  sqlite3_bind_text (stmt, 5, ths->m_addr, -1, SQLITE_STATIC);
109  if( sqlite3_step(stmt) != SQLITE_DONE ) {
110  goto cleanup;
111  }
112  }
113  else if( ths->m_to_save&MRA_SAVE_LAST_SEEN )
114  {
115  stmt = mrsqlite3_predefine__(sql, UPDATE_acpeerstates_SET_l_WHERE_a,
116  "UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=? WHERE addr=?;");
117  sqlite3_bind_int64(stmt, 1, ths->m_last_seen);
118  sqlite3_bind_int64(stmt, 2, ths->m_last_seen_autocrypt);
119  sqlite3_bind_text (stmt, 3, ths->m_addr, -1, SQLITE_STATIC);
120  if( sqlite3_step(stmt) != SQLITE_DONE ) {
121  goto cleanup;
122  }
123  }
124 
125  success = 1;
126 
127 cleanup:
128  return success;
129 }
130 
131 
132 /*******************************************************************************
133  * Main interface
134  ******************************************************************************/
135 
136 
137 mrapeerstate_t* mrapeerstate_new()
138 {
139  mrapeerstate_t* ths = NULL;
140 
141  if( (ths=calloc(1, sizeof(mrapeerstate_t)))==NULL ) {
142  exit(43); /* cannot allocate little memory, unrecoverable error */
143  }
144 
145  ths->m_public_key = mrkey_new();
146 
147  return ths;
148 }
149 
150 
151 void mrapeerstate_unref(mrapeerstate_t* ths)
152 {
153  if( ths==NULL ) {
154  return;
155  }
156 
157  free(ths->m_addr);
158  mrkey_unref(ths->m_public_key);
159  free(ths);
160 }
161 
162 
163 /*******************************************************************************
164  * Change state
165  ******************************************************************************/
166 
167 
168 int mrapeerstate_init_from_header(mrapeerstate_t* ths, const mraheader_t* header, time_t message_time)
169 {
170  if( ths == NULL || header == NULL ) {
171  return 0;
172  }
173 
174  mrapeerstate_empty(ths);
175  ths->m_addr = safe_strdup(header->m_addr);
176  ths->m_last_seen = message_time;
177  ths->m_last_seen_autocrypt = message_time;
178  ths->m_to_save = MRA_SAVE_ALL;
179  ths->m_prefer_encrypt = header->m_prefer_encrypt;
180  mrkey_set_from_key(ths->m_public_key, header->m_public_key);
181  return 1;
182 }
183 
184 
185 int mrapeerstate_degrade_encryption(mrapeerstate_t* ths, time_t message_time)
186 {
187  if( ths==NULL ) {
188  return 0;
189  }
190 
191  ths->m_prefer_encrypt = MRA_PE_RESET;
192  ths->m_last_seen = message_time; /*last_seen_autocrypt is not updated as there was not Autocrypt:-header seen*/
193  ths->m_to_save = MRA_SAVE_ALL;
194  return 1;
195 }
196 
197 
198 int mrapeerstate_apply_header(mrapeerstate_t* ths, const mraheader_t* header, time_t message_time)
199 {
200  if( ths==NULL || header==NULL
201  || ths->m_addr==NULL
202  || header->m_addr==NULL || header->m_public_key->m_binary==NULL
203  || strcasecmp(ths->m_addr, header->m_addr)!=0 ) {
204  return 0;
205  }
206 
207  if( message_time > ths->m_last_seen_autocrypt )
208  {
209  ths->m_last_seen = message_time;
210  ths->m_last_seen_autocrypt = message_time;
211  ths->m_to_save |= MRA_SAVE_LAST_SEEN;
212 
213  if( (header->m_prefer_encrypt==MRA_PE_MUTUAL || header->m_prefer_encrypt==MRA_PE_NOPREFERENCE) /*this also switches from MRA_PE_RESET to MRA_PE_NOPREFERENCE, which is just fine as the function is only called _if_ the Autocrypt:-header is preset at all */
214  && header->m_prefer_encrypt != ths->m_prefer_encrypt )
215  {
216  ths->m_prefer_encrypt = header->m_prefer_encrypt;
217  ths->m_to_save |= MRA_SAVE_ALL;
218  }
219 
220  if( !mrkey_equals(ths->m_public_key, header->m_public_key) )
221  {
222  mrkey_set_from_key(ths->m_public_key, header->m_public_key);
223  ths->m_to_save |= MRA_SAVE_ALL;
224  }
225  }
226 
227  return ths->m_to_save? 1 : 0;
228 }
229 
- - - - diff --git a/docs/user/html/mrchat_8c_source.html b/docs/user/html/mrchat_8c_source.html deleted file mode 100644 index 5f23ba36..00000000 --- a/docs/user/html/mrchat_8c_source.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrchat.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrchat.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrapeerstate.h"
25 #include "mrjob.h"
26 #include "mrsmtp.h"
27 #include "mrimap.h"
28 #include "mrmimefactory.h"
29 
30 
31 int mrchat_update_param__(mrchat_t* ths)
32 {
33  int success = 0;
34  sqlite3_stmt* stmt = mrsqlite3_prepare_v2_(ths->m_mailbox->m_sql, "UPDATE chats SET param=? WHERE id=?");
35  sqlite3_bind_text(stmt, 1, ths->m_param->m_packed, -1, SQLITE_STATIC);
36  sqlite3_bind_int (stmt, 2, ths->m_id);
37  success = sqlite3_step(stmt)==SQLITE_DONE? 1 : 0;
38  sqlite3_finalize(stmt);
39  return success;
40 }
41 
42 
43 static int mrchat_set_from_stmt__(mrchat_t* ths, sqlite3_stmt* row)
44 {
45  int row_offset = 0;
46  const char* draft_text;
47 
48  if( ths == NULL || row == NULL ) {
49  return 0;
50  }
51 
52  mrchat_empty(ths);
53 
54  #define MR_CHAT_FIELDS " c.id,c.type,c.name, c.draft_timestamp,c.draft_txt,c.grpid,c.param,c.archived "
55  ths->m_id = sqlite3_column_int (row, row_offset++); /* the columns are defined in MR_CHAT_FIELDS */
56  ths->m_type = sqlite3_column_int (row, row_offset++);
57  ths->m_name = safe_strdup((char*)sqlite3_column_text (row, row_offset++));
58  ths->m_draft_timestamp = sqlite3_column_int64(row, row_offset++);
59  draft_text = (const char*)sqlite3_column_text (row, row_offset++);
60  ths->m_grpid = safe_strdup((char*)sqlite3_column_text (row, row_offset++));
61  mrparam_set_packed(ths->m_param, (char*)sqlite3_column_text (row, row_offset++));
62  ths->m_archived = sqlite3_column_int (row, row_offset++);
63 
64  /* We leave a NULL-pointer for the very usual situation of "no draft".
65  Also make sure, m_draft_text and m_draft_timestamp are set together */
66  if( ths->m_draft_timestamp && draft_text && draft_text[0] ) {
67  ths->m_draft_text = safe_strdup(draft_text);
68  }
69  else {
70  ths->m_draft_timestamp = 0;
71  }
72 
73  /* correct the title of some special groups */
74  if( ths->m_id == MR_CHAT_ID_DEADDROP ) {
75  free(ths->m_name);
76  ths->m_name = mrstock_str(MR_STR_DEADDROP);
77  }
78  else if( ths->m_id == MR_CHAT_ID_ARCHIVED_LINK ) {
79  free(ths->m_name);
80  char* tempname = mrstock_str(MR_STR_ARCHIVEDCHATS);
81  ths->m_name = mr_mprintf("%s (%i)", tempname, mrmailbox_get_archived_count__(ths->m_mailbox));
82  free(tempname);
83  }
84  else if( ths->m_id == MR_CHAT_ID_STARRED ) {
85  free(ths->m_name);
86  ths->m_name = mrstock_str(MR_STR_STARREDMSGS);
87  }
88 
89  return row_offset; /* success, return the next row offset */
90 }
91 
92 
100 int mrchat_load_from_db__(mrchat_t* ths, uint32_t id)
101 {
102  sqlite3_stmt* stmt;
103 
104  if( ths==NULL ) {
105  return 0;
106  }
107 
108  mrchat_empty(ths);
109 
110  stmt = mrsqlite3_predefine__(ths->m_mailbox->m_sql, SELECT_itndd_FROM_chats_WHERE_i,
111  "SELECT " MR_CHAT_FIELDS " FROM chats c WHERE c.id=?;");
112  sqlite3_bind_int(stmt, 1, id);
113 
114  if( sqlite3_step(stmt) != SQLITE_ROW ) {
115  return 0;
116  }
117 
118  if( !mrchat_set_from_stmt__(ths, stmt) ) {
119  return 0;
120  }
121 
122  return 1;
123 }
124 
125 
135 mrchat_t* mrchat_new(mrmailbox_t* mailbox)
136 {
137  mrchat_t* ths = NULL;
138 
139  if( mailbox == NULL || (ths=calloc(1, sizeof(mrchat_t)))==NULL ) {
140  exit(14); /* cannot allocate little memory, unrecoverable error */
141  }
142 
143  ths->m_mailbox = mailbox;
144  ths->m_type = MR_CHAT_TYPE_UNDEFINED;
145  ths->m_param = mrparam_new();
146 
147  return ths;
148 }
149 
150 
161 {
162  if( chat==NULL ) {
163  return;
164  }
165 
166  mrchat_empty(chat);
167  mrparam_unref(chat->m_param);
168  free(chat);
169 }
170 
171 
182 {
183  if( ths == NULL ) {
184  return;
185  }
186 
187  free(ths->m_name);
188  ths->m_name = NULL;
189 
190  ths->m_draft_timestamp = 0;
191 
192  free(ths->m_draft_text);
193  ths->m_draft_text = NULL;
194 
195  ths->m_type = MR_CHAT_TYPE_UNDEFINED;
196  ths->m_id = 0;
197 
198  free(ths->m_grpid);
199  ths->m_grpid = NULL;
200 
201  mrparam_set_packed(ths->m_param, NULL);
202 }
203 
204 
216 {
217  /* returns either the address or the number of chat members */
218  char* ret = NULL;
219  sqlite3_stmt* stmt;
220 
221  if( chat == NULL ) {
222  return safe_strdup("Err");
223  }
224 
225  if( chat->m_type == MR_CHAT_TYPE_NORMAL )
226  {
227  int r;
228  mrsqlite3_lock(chat->m_mailbox->m_sql);
229 
230  stmt = mrsqlite3_predefine__(chat->m_mailbox->m_sql, SELECT_a_FROM_chats_contacts_WHERE_i,
231  "SELECT c.addr FROM chats_contacts cc "
232  " LEFT JOIN contacts c ON c.id=cc.contact_id "
233  " WHERE cc.chat_id=?;");
234  sqlite3_bind_int(stmt, 1, chat->m_id);
235 
236  r = sqlite3_step(stmt);
237  if( r == SQLITE_ROW ) {
238  ret = safe_strdup((const char*)sqlite3_column_text(stmt, 0));
239  }
240 
241  mrsqlite3_unlock(chat->m_mailbox->m_sql);
242  }
243  else if( chat->m_type == MR_CHAT_TYPE_GROUP )
244  {
245  int cnt = 0;
246  if( chat->m_id == MR_CHAT_ID_DEADDROP )
247  {
248  mrsqlite3_lock(chat->m_mailbox->m_sql);
249 
250  stmt = mrsqlite3_predefine__(chat->m_mailbox->m_sql, SELECT_COUNT_DISTINCT_f_FROM_msgs_WHERE_c,
251  "SELECT COUNT(DISTINCT from_id) FROM msgs WHERE chat_id=?;");
252  sqlite3_bind_int(stmt, 1, chat->m_id);
253  if( sqlite3_step(stmt) == SQLITE_ROW ) {
254  cnt = sqlite3_column_int(stmt, 0);
255  ret = mrstock_str_repl_pl(MR_STR_CONTACT, cnt);
256  }
257 
258  mrsqlite3_unlock(chat->m_mailbox->m_sql);
259  }
260  else
261  {
262  mrsqlite3_lock(chat->m_mailbox->m_sql);
263 
264  cnt = mrmailbox_get_chat_contact_count__(chat->m_mailbox, chat->m_id);
265  ret = mrstock_str_repl_pl(MR_STR_MEMBER, cnt /*SELF is included in group chats (if not removed)*/);
266 
267  mrsqlite3_unlock(chat->m_mailbox->m_sql);
268  }
269  }
270 
271  return ret? ret : safe_strdup("Err");
272 }
273 
274 
275 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
-
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
-
char * m_draft_text
NULL if unset.
Definition: mrchat.h:57
-
mrmailbox_t * m_mailbox
!= NULL
Definition: mrchat.h:58
-
mrparam_t * m_param
!= NULL
Definition: mrchat.h:60
-
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
-
int m_archived
1=chat archived, this state should always be shown the UI, eg.
Definition: mrchat.h:59
-
char * m_name
NULL if unset.
Definition: mrchat.h:55
-
void mrchat_empty(mrchat_t *ths)
Empty a chat object.
Definition: mrchat.c:181
-
time_t m_draft_timestamp
0 if there is no draft
Definition: mrchat.h:56
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
char * mrchat_get_subtitle(mrchat_t *chat)
Get a subtitle for a chat.
Definition: mrchat.c:215
-
- - - - diff --git a/docs/user/html/mrchatlist_8c_source.html b/docs/user/html/mrchatlist_8c_source.html deleted file mode 100644 index 38dbe224..00000000 --- a/docs/user/html/mrchatlist_8c_source.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrchatlist.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrchatlist.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 
25 
35 mrchatlist_t* mrchatlist_new(mrmailbox_t* mailbox)
36 {
37  mrchatlist_t* ths = NULL;
38 
39  if( (ths=calloc(1, sizeof(mrchatlist_t)))==NULL ) {
40  exit(20);
41  }
42 
43  ths->m_mailbox = mailbox;
44  if( (ths->m_chatNlastmsg_ids=carray_new(128))==NULL ) {
45  exit(32);
46  }
47 
48  return ths;
49 }
50 
51 
63 {
64  if( chatlist==NULL ) {
65  return;
66  }
67 
68  mrchatlist_empty(chatlist);
69  carray_free(chatlist->m_chatNlastmsg_ids);
70  free(chatlist);
71 }
72 
73 
84 {
85  if( chatlist ) {
86  chatlist->m_cnt = 0;
87  carray_set_size(chatlist->m_chatNlastmsg_ids, 0);
88  }
89 }
90 
91 
102 {
103  if( chatlist == NULL ) {
104  return 0;
105  }
106 
107  return chatlist->m_cnt;
108 }
109 
110 
121 uint32_t mrchatlist_get_chat_id(mrchatlist_t* ths, size_t index)
122 {
123  if( ths == NULL || ths->m_chatNlastmsg_ids == NULL || index >= ths->m_cnt ) {
124  return 0;
125  }
126 
127  return (uint32_t)(uintptr_t)carray_get(ths->m_chatNlastmsg_ids, index*MR_CHATLIST_IDS_PER_RESULT);
128 }
129 
130 
131 mrchat_t* mrchatlist_get_chat_by_index(mrchatlist_t* ths, size_t index) /* deprecated */
132 {
133  if( ths == NULL || ths->m_chatNlastmsg_ids == NULL || index >= ths->m_cnt ) {
134  return 0;
135  }
136 
137  return mrmailbox_get_chat(ths->m_mailbox, (uint32_t)(uintptr_t)carray_get(ths->m_chatNlastmsg_ids, index*MR_CHATLIST_IDS_PER_RESULT));
138 }
139 
140 
151 uint32_t mrchatlist_get_msg_id(mrchatlist_t* ths, size_t index)
152 {
153  if( ths == NULL || ths->m_chatNlastmsg_ids == NULL || index >= ths->m_cnt ) {
154  return 0;
155  }
156 
157  return (uint32_t)(uintptr_t)carray_get(ths->m_chatNlastmsg_ids, index*MR_CHATLIST_IDS_PER_RESULT+1);
158 }
159 
160 
161 mrmsg_t* mrchatlist_get_msg_by_index(mrchatlist_t* ths, size_t index) /* deprecated */
162 {
163  if( ths == NULL || ths->m_chatNlastmsg_ids == NULL || index >= ths->m_cnt ) {
164  return 0;
165  }
166 
167  return mrmailbox_get_msg(ths->m_mailbox, (uint32_t)(uintptr_t)carray_get(ths->m_chatNlastmsg_ids, index*MR_CHATLIST_IDS_PER_RESULT+1));
168 }
169 
170 
201 mrpoortext_t* mrchatlist_get_summary(mrchatlist_t* chatlist, size_t index, mrchat_t* chat /*may be NULL*/)
202 {
203  /* The summary is created by the chat, not by the last message.
204  This is because we may want to display drafts here or stuff as
205  "is typing".
206  Also, sth. as "No messages" would not work if the summary comes from a
207  message. */
208 
209  mrpoortext_t* ret = mrpoortext_new(); /* the function never returns NULL */
210  int locked = 0;
211  uint32_t lastmsg_id = 0;
212  mrmsg_t* lastmsg = NULL;
213  mrcontact_t* lastcontact = NULL;
214  mrchat_t* chat_to_delete = NULL;
215 
216  if( chatlist == NULL || index >= chatlist->m_cnt ) {
217  ret->m_text2 = safe_strdup("ErrBadChatlistIndex");
218  goto cleanup;
219  }
220 
221  lastmsg_id = (uint32_t)(uintptr_t)carray_get(chatlist->m_chatNlastmsg_ids, index*MR_CHATLIST_IDS_PER_RESULT+1);
222 
223  /* load data from database */
224  mrsqlite3_lock(chatlist->m_mailbox->m_sql);
225  locked = 1;
226 
227  if( chat==NULL ) {
228  chat = mrchat_new(chatlist->m_mailbox);
229  chat_to_delete = chat;
230  if( !mrchat_load_from_db__(chat, (uint32_t)(uintptr_t)carray_get(chatlist->m_chatNlastmsg_ids, index*MR_CHATLIST_IDS_PER_RESULT)) ) {
231  ret->m_text2 = safe_strdup("ErrCannotReadChat");
232  goto cleanup;
233  }
234  }
235 
236  if( lastmsg_id )
237  {
238 
239  lastmsg = mrmsg_new();
240  mrmsg_load_from_db__(lastmsg, chatlist->m_mailbox, lastmsg_id);
241 
242  if( lastmsg->m_from_id != MR_CONTACT_ID_SELF && chat->m_type == MR_CHAT_TYPE_GROUP )
243  {
244  lastcontact = mrcontact_new();
245  mrcontact_load_from_db__(lastcontact, chatlist->m_mailbox->m_sql, lastmsg->m_from_id);
246  }
247 
248  }
249 
250  mrsqlite3_unlock(chatlist->m_mailbox->m_sql);
251  locked = 0;
252 
253  if( chat->m_id == MR_CHAT_ID_ARCHIVED_LINK )
254  {
255  ret->m_text2 = safe_strdup(NULL);
256  }
257  else if( chat->m_draft_timestamp
258  && chat->m_draft_text
259  && (lastmsg==NULL || chat->m_draft_timestamp>lastmsg->m_timestamp) )
260  {
261  /* show the draft as the last message */
262  ret->m_text1 = mrstock_str(MR_STR_DRAFT);
264 
265  ret->m_text2 = safe_strdup(chat->m_draft_text);
266  mr_truncate_n_unwrap_str(ret->m_text2, MR_SUMMARY_CHARACTERS, 1);
267 
268  ret->m_timestamp = chat->m_draft_timestamp;
269  }
270  else if( lastmsg == NULL || lastmsg->m_from_id == 0 )
271  {
272  /* no messages */
273  ret->m_text2 = mrstock_str(MR_STR_NOMESSAGES);
274  }
275  else
276  {
277  /* show the last message */
278  mrpoortext_fill(ret, lastmsg, chat, lastcontact);
279  }
280 
281 cleanup:
282  if( locked ) { mrsqlite3_unlock(chatlist->m_mailbox->m_sql); }
283  mrmsg_unref(lastmsg);
284  mrcontact_unref(lastcontact);
285  mrchat_unref(chat_to_delete);
286  return ret;
287 }
288 
289 
297 int mrchatlist_load_from_db__(mrchatlist_t* ths, int listflags, const char* query__)
298 {
299  int success = 0;
300  int add_archived_link_item = 0;
301  sqlite3_stmt* stmt = NULL;
302  char* strLikeCmd = NULL, *query = NULL;
303 
304  if( ths == NULL || ths->m_mailbox == NULL ) {
305  goto cleanup;
306  }
307 
308  mrchatlist_empty(ths);
309 
310  /* select example with left join and minimum: http://stackoverflow.com/questions/7588142/mysql-left-join-min */
311  #define QUR1 "SELECT c.id, m.id FROM chats c " \
312  " LEFT JOIN msgs m ON (c.id=m.chat_id AND m.timestamp=(SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id)) " \
313  " WHERE c.id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) " AND c.blocked=0"
314  #define QUR2 " GROUP BY c.id " /* GROUP BY is needed as there may be several messages with the same timestamp */ \
315  " ORDER BY MAX(c.draft_timestamp, IFNULL(m.timestamp,0)) DESC,m.id DESC;" /* the list starts with the newest chats */
316 
317  if( listflags & MR_GCL_ARCHIVED_ONLY )
318  {
319  /* show archived chats */
320  stmt = mrsqlite3_predefine__(ths->m_mailbox->m_sql, SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_archived,
321  QUR1 " AND c.archived=1 " QUR2);
322  }
323  else if( query__==NULL )
324  {
325  /* show normal chatlist */
326  if( !(listflags & MR_GCL_NO_SPECIALS) ) {
327  uint32_t last_deaddrop_fresh_msg_id = mrmailbox_get_last_deaddrop_fresh_msg__(ths->m_mailbox);
328  if( last_deaddrop_fresh_msg_id > 0 ) {
329  carray_add(ths->m_chatNlastmsg_ids, (void*)(uintptr_t)MR_CHAT_ID_DEADDROP, NULL); /* show deaddrop with the last fresh message */
330  carray_add(ths->m_chatNlastmsg_ids, (void*)(uintptr_t)last_deaddrop_fresh_msg_id, NULL);
331  }
332  add_archived_link_item = 1;
333  }
334 
335  stmt = mrsqlite3_predefine__(ths->m_mailbox->m_sql, SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_unarchived,
336  QUR1 " AND c.archived=0 " QUR2);
337  }
338  else
339  {
340  /* show chatlist filtered by a search string, this includes archived and unarchived */
341  query = safe_strdup(query__);
342  mr_trim(query);
343  if( query[0]==0 ) {
344  success = 1; /*empty result*/
345  goto cleanup;
346  }
347  strLikeCmd = mr_mprintf("%%%s%%", query);
348  stmt = mrsqlite3_predefine__(ths->m_mailbox->m_sql, SELECT_ii_FROM_chats_LEFT_JOIN_msgs_WHERE_query,
349  QUR1 " AND c.name LIKE ? " QUR2);
350  sqlite3_bind_text(stmt, 1, strLikeCmd, -1, SQLITE_STATIC);
351  }
352 
353  while( sqlite3_step(stmt) == SQLITE_ROW )
354  {
355  carray_add(ths->m_chatNlastmsg_ids, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
356  carray_add(ths->m_chatNlastmsg_ids, (void*)(uintptr_t)sqlite3_column_int(stmt, 1), NULL);
357  }
358 
359  if( add_archived_link_item && mrmailbox_get_archived_count__(ths->m_mailbox)>0 )
360  {
361  carray_add(ths->m_chatNlastmsg_ids, (void*)(uintptr_t)MR_CHAT_ID_ARCHIVED_LINK, NULL);
362  carray_add(ths->m_chatNlastmsg_ids, (void*)(uintptr_t)0, NULL);
363  }
364 
365  ths->m_cnt = carray_count(ths->m_chatNlastmsg_ids)/MR_CHATLIST_IDS_PER_RESULT;
366  success = 1;
367 
368 cleanup:
369  free(query);
370  free(strLikeCmd);
371  return success;
372 }
mrmailbox_t * m_mailbox
The mailbox, the chatlist belongs to.
Definition: mrchatlist.h:44
-
uint32_t mrchatlist_get_msg_id(mrchatlist_t *ths, size_t index)
Get a single message ID of a chatlist.
Definition: mrchatlist.c:151
-
mrchat_t * mrmailbox_get_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Get a chat object of type mrchat_t by a chat_id.
Definition: mrmailbox.c:1709
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
An object representing a single chatlist in memory.
Definition: mrchatlist.h:42
-
char * m_text2
may be NULL
Definition: mrpoortext.h:39
-
mrpoortext_t * mrchatlist_get_summary(mrchatlist_t *chatlist, size_t index, mrchat_t *chat)
Get a summary for a chatlist index.
Definition: mrchatlist.c:201
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
-
void mrchatlist_empty(mrchatlist_t *chatlist)
Empty a chatlist object.
Definition: mrchatlist.c:83
-
time_t m_timestamp
may be 0
Definition: mrpoortext.h:40
-
mrcontact_t * mrcontact_new()
Create a new contact object in memory.
Definition: mrcontact.c:32
-
mrmsg_t * mrmsg_new()
Create new message object.
Definition: mrmsg.c:41
-
char * m_text1
may be NULL
Definition: mrpoortext.h:38
-
#define MR_TEXT1_DRAFT
Definition: mrpoortext.h:46
-
void mrcontact_unref(mrcontact_t *ths)
Free a contact object.
Definition: mrcontact.c:49
-
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
-
char * m_draft_text
NULL if unset.
Definition: mrchat.h:57
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
int m_text1_meaning
One of MR_TEXT1_NORMAL, MR_TEXT1_DRAFT, MR_TEXT1_USERNAME or MR_TEXT1_SELF.
Definition: mrpoortext.h:37
-
uint32_t m_from_id
Contact ID of the sender.
Definition: mrmsg.h:47
-
mrmsg_t * mrmailbox_get_msg(mrmailbox_t *mailbox, uint32_t msg_id)
Get a single message object of the type mrmsg_t.
Definition: mrmailbox.c:4701
-
void mrmsg_unref(mrmsg_t *msg)
Free an mrmsg_t object created eg.
Definition: mrmsg.c:66
-
time_t m_draft_timestamp
0 if there is no draft
Definition: mrchat.h:56
-
size_t mrchatlist_get_cnt(mrchatlist_t *chatlist)
Find out the number of chats in a chatlist.
Definition: mrchatlist.c:101
-
uint32_t mrchatlist_get_chat_id(mrchatlist_t *ths, size_t index)
Get a single chat ID of a chatlist.
Definition: mrchatlist.c:121
-
void mrchatlist_unref(mrchatlist_t *chatlist)
Free a mrchatlist_t object as created eg.
Definition: mrchatlist.c:62
-
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
- - - - diff --git a/docs/user/html/mrcontact_8c_source.html b/docs/user/html/mrcontact_8c_source.html deleted file mode 100644 index 938bf311..00000000 --- a/docs/user/html/mrcontact_8c_source.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrcontact.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrcontact.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrcontact.h"
25 
26 
33 {
34  mrcontact_t* ths = NULL;
35 
36  if( (ths=calloc(1, sizeof(mrcontact_t)))==NULL ) {
37  exit(19); /* cannot allocate little memory, unrecoverable error */
38  }
39 
40  return ths;
41 }
42 
43 
50 {
51  if( ths==NULL ) {
52  return;
53  }
54 
55  mrcontact_empty(ths);
56  free(ths);
57 }
58 
59 
66 {
67  if( ths == NULL ) {
68  return;
69  }
70 
71  ths->m_id = 0;
72 
73  free(ths->m_name); /* it is safe to call free(NULL) */
74  ths->m_name = NULL;
75 
76  free(ths->m_authname);
77  ths->m_authname = NULL;
78 
79  free(ths->m_addr);
80  ths->m_addr = NULL;
81 
82  ths->m_origin = 0;
83  ths->m_blocked = 0;
84 }
85 
86 
99 char* mrcontact_get_first_name(const char* full_name)
100 {
101  char* first_name = safe_strdup(full_name);
102  char* p1 = strchr(first_name, ' ');
103  if( p1 ) {
104  *p1 = 0;
105  mr_rtrim(first_name);
106  if( first_name[0] == 0 ) { /*empty result? use the original string in this case */
107  free(first_name);
108  first_name = safe_strdup(full_name);
109  }
110  }
111 
112  return first_name;
113 }
114 
115 
130 void mrcontact_normalize_name(char* full_name)
131 {
132  if( full_name == NULL ) {
133  return; /* error, however, this can be treated as documented behaviour */
134  }
135 
136  mr_trim(full_name); /* remove spaces around possible quotes */
137  int len = strlen(full_name);
138  if( len > 0 ) {
139  char firstchar = full_name[0], lastchar = full_name[len-1];
140  if( (firstchar=='\'' && lastchar=='\'')
141  || (firstchar=='"' && lastchar=='"' )
142  || (firstchar=='<' && lastchar=='>' ) ) {
143  full_name[0] = ' ';
144  full_name[len-1] = ' '; /* the string is trimmed later again */
145  }
146  }
147 
148  char* p1 = strchr(full_name, ',');
149  if( p1 ) {
150  *p1 = 0;
151  char* last_name = safe_strdup(full_name);
152  char* first_name = safe_strdup(p1+1);
153  mr_trim(last_name);
154  mr_trim(first_name);
155  strcpy(full_name, first_name);
156  strcat(full_name, " ");
157  strcat(full_name, last_name);
158  free(last_name);
159  free(first_name);
160  }
161  else {
162  mr_trim(full_name);
163  }
164 }
165 
166 
174 int mrcontact_load_from_db__(mrcontact_t* ths, mrsqlite3_t* sql, uint32_t contact_id)
175 {
176  int success = 0;
177  sqlite3_stmt* stmt;
178 
179  if( ths == NULL || sql == NULL ) {
180  return 0;
181  }
182 
183  mrcontact_empty(ths);
184 
185  stmt = mrsqlite3_predefine__(sql, SELECT_naob_FROM_contacts_i,
186  "SELECT name, addr, origin, blocked, authname FROM contacts WHERE id=?;");
187  sqlite3_bind_int(stmt, 1, contact_id);
188  if( sqlite3_step(stmt) != SQLITE_ROW ) {
189  goto cleanup;
190  }
191 
192  ths->m_id = contact_id;
193  ths->m_name = safe_strdup((char*)sqlite3_column_text (stmt, 0));
194  ths->m_addr = safe_strdup((char*)sqlite3_column_text (stmt, 1));
195  ths->m_origin = sqlite3_column_int (stmt, 2);
196  ths->m_blocked = sqlite3_column_int (stmt, 3);
197  ths->m_authname = safe_strdup((char*)sqlite3_column_text (stmt, 4));
198 
199  /* success */
200  success = 1;
201 
202  /* cleanup */
203 cleanup:
204  return success;
205 }
char * mrcontact_get_first_name(const char *full_name)
Get the first name.
Definition: mrcontact.c:99
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
void mrcontact_empty(mrcontact_t *ths)
Empty a contact object.
Definition: mrcontact.c:65
-
mrcontact_t * mrcontact_new()
Create a new contact object in memory.
Definition: mrcontact.c:32
-
void mrcontact_unref(mrcontact_t *ths)
Free a contact object.
Definition: mrcontact.c:49
-
void mrcontact_normalize_name(char *full_name)
Normalize a name in-place.
Definition: mrcontact.c:130
-
char * m_authname
may be NULL or empty, this is the name authorized by the sender, only this name may be speaded to oth...
Definition: mrcontact.h:46
-
int m_blocked
Blocked state.
Definition: mrcontact.h:48
-
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
-
char * m_name
may be NULL or empty, this name should not be spreaded as it may be "Daddy" and so on; initially set ...
Definition: mrcontact.h:45
-
uint32_t m_id
The contact ID.
Definition: mrcontact.h:43
-
- - - - diff --git a/docs/user/html/mrdehtml_8c_source.html b/docs/user/html/mrdehtml_8c_source.html deleted file mode 100644 index 30c5290f..00000000 --- a/docs/user/html/mrdehtml_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrdehtml.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrdehtml.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include "mrmailbox.h"
26 #include "mrdehtml.h"
27 #include "mrsaxparser.h"
28 #include "mrtools.h"
29 
30 
31 
32 typedef struct dehtml_t
33 {
34  mrstrbuilder_t m_strbuilder;
35 
36  #define DO_NOT_ADD 0
37  #define DO_ADD_REMOVE_LINEENDS 1
38  #define DO_ADD_PRESERVE_LINEENDS 2
39  int m_add_text;
40  char* m_last_href;
41 
42 } dehtml_t;
43 
44 
45 static void dehtml_starttag_cb(void* userdata, const char* tag, char** attr)
46 {
47  dehtml_t* dehtml = (dehtml_t*)userdata;
48 
49  if( strcmp(tag, "p")==0 || strcmp(tag, "div")==0 || strcmp(tag, "table")==0 || strcmp(tag, "td")==0 )
50  {
51  mrstrbuilder_cat(&dehtml->m_strbuilder, "\n\n");
52  dehtml->m_add_text = DO_ADD_REMOVE_LINEENDS;
53  }
54  else if( strcmp(tag, "br")==0 )
55  {
56  mrstrbuilder_cat(&dehtml->m_strbuilder, "\n");
57  dehtml->m_add_text = DO_ADD_REMOVE_LINEENDS;
58  }
59  else if( strcmp(tag, "style")==0 || strcmp(tag, "script")==0 || strcmp(tag, "title")==0 )
60  {
61  dehtml->m_add_text = DO_NOT_ADD;
62  }
63  else if( strcmp(tag, "pre")==0 )
64  {
65  mrstrbuilder_cat(&dehtml->m_strbuilder, "\n\n");
66  dehtml->m_add_text = DO_ADD_PRESERVE_LINEENDS;
67  }
68  else if( strcmp(tag, "a")==0 )
69  {
70  free(dehtml->m_last_href);
71  dehtml->m_last_href = strdup_keep_null(mrattr_find(attr, "href"));
72  if( dehtml->m_last_href ) {
73  mrstrbuilder_cat(&dehtml->m_strbuilder, "[");
74  }
75  }
76  else if( strcmp(tag, "b")==0 || strcmp(tag, "strong")==0 )
77  {
78  mrstrbuilder_cat(&dehtml->m_strbuilder, "*");
79  }
80  else if( strcmp(tag, "i")==0 || strcmp(tag, "em")==0 )
81  {
82  mrstrbuilder_cat(&dehtml->m_strbuilder, "_");
83  }
84 }
85 
86 
87 static void dehtml_text_cb(void* userdata, const char* text, int len)
88 {
89  dehtml_t* dehtml = (dehtml_t*)userdata;
90 
91  if( dehtml->m_add_text != DO_NOT_ADD )
92  {
93  char* last_added = mrstrbuilder_cat(&dehtml->m_strbuilder, text);
94 
95  if( dehtml->m_add_text==DO_ADD_REMOVE_LINEENDS )
96  {
97  unsigned char* p = (unsigned char*)last_added;
98  while( *p ) {
99  if( *p=='\n' ) {
100  int last_is_lineend = 1; /* avoid converting `text1<br>\ntext2` to `text1\n text2` (`\r` is removed later) */
101  const unsigned char* p2 = p-1;
102  while( p2>=(const unsigned char*)dehtml->m_strbuilder.m_buf ) {
103  if( *p2 == '\r' ) {
104  }
105  else if( *p2 == '\n' ) {
106  break;
107  }
108  else {
109  last_is_lineend = 0;
110  break;
111  }
112  p2--;
113  }
114  *p = last_is_lineend? '\r' : ' ';
115  }
116  p++;
117  }
118  }
119  }
120 }
121 
122 
123 static void dehtml_endtag_cb(void* userdata, const char* tag)
124 {
125  dehtml_t* dehtml = (dehtml_t*)userdata;
126 
127  if( strcmp(tag, "p")==0 || strcmp(tag, "div")==0 || strcmp(tag, "table")==0 || strcmp(tag, "td")==0
128  || strcmp(tag, "style")==0 || strcmp(tag, "script")==0 || strcmp(tag, "title")==0
129  || strcmp(tag, "pre")==0 )
130  {
131  mrstrbuilder_cat(&dehtml->m_strbuilder, "\n\n"); /* do not expect an starting block element (which, of course, should come right now) */
132  dehtml->m_add_text = DO_ADD_REMOVE_LINEENDS;
133  }
134  else if( strcmp(tag, "a")==0 )
135  {
136  if( dehtml->m_last_href ) {
137  mrstrbuilder_cat(&dehtml->m_strbuilder, "](");
138  mrstrbuilder_cat(&dehtml->m_strbuilder, dehtml->m_last_href);
139  mrstrbuilder_cat(&dehtml->m_strbuilder, ")");
140  free(dehtml->m_last_href);
141  dehtml->m_last_href = NULL;
142  }
143  }
144  else if( strcmp(tag, "b")==0 || strcmp(tag, "strong")==0 )
145  {
146  mrstrbuilder_cat(&dehtml->m_strbuilder, "*");
147  }
148  else if( strcmp(tag, "i")==0 || strcmp(tag, "em")==0 )
149  {
150  mrstrbuilder_cat(&dehtml->m_strbuilder, "_");
151  }
152 }
153 
154 
155 char* mr_dehtml(char* buf_terminated)
156 {
157  mr_trim(buf_terminated);
158  if( buf_terminated[0] == 0 ) {
159  return safe_strdup(""); /* support at least empty HTML-messages; for empty messages, we'll replace the message by the subject later */
160  }
161  else {
162  dehtml_t dehtml;
163  mrsaxparser_t saxparser;
164 
165  memset(&dehtml, 0, sizeof(dehtml_t));
166  dehtml.m_add_text = DO_ADD_REMOVE_LINEENDS;
167  mrstrbuilder_init(&dehtml.m_strbuilder);
168 
169  mrsaxparser_init(&saxparser, &dehtml);
170  mrsaxparser_set_tag_handler(&saxparser, dehtml_starttag_cb, dehtml_endtag_cb);
171  mrsaxparser_set_text_handler(&saxparser, dehtml_text_cb);
172  mrsaxparser_parse(&saxparser, buf_terminated);
173 
174  free(dehtml.m_last_href);
175  return dehtml.m_strbuilder.m_buf;
176  }
177 }
178 
179 
- - - - diff --git a/docs/user/html/mrimap_8c_source.html b/docs/user/html/mrimap_8c_source.html deleted file mode 100644 index c6610b61..00000000 --- a/docs/user/html/mrimap_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrimap.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrimap.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <stdlib.h>
24 #include <libetpan/libetpan.h>
25 #include <sys/stat.h>
26 #include <string.h>
27 #include <unistd.h> /* for sleep() */
28 #include "mrmailbox_internal.h"
29 #include "mrimap.h"
30 #include "mrosnative.h"
31 #include "mrloginparam.h"
32 
33 #define LOCK_HANDLE pthread_mutex_lock(&ths->m_hEtpanmutex); mrmailbox_wake_lock(ths->m_mailbox); handle_locked = 1;
34 #define UNLOCK_HANDLE if( handle_locked ) { mrmailbox_wake_unlock(ths->m_mailbox); pthread_mutex_unlock(&ths->m_hEtpanmutex); handle_locked = 0; }
35 
36 #define BLOCK_IDLE pthread_mutex_lock(&ths->m_idlemutex); idle_blocked = 1;
37 #define UNBLOCK_IDLE if( idle_blocked ) { pthread_mutex_unlock(&ths->m_idlemutex); idle_blocked = 0; }
38 #define INTERRUPT_IDLE \
39  if( ths && ths->m_can_idle && ths->m_hEtpan && ths->m_hEtpan->imap_stream ) { \
40  if( pthread_mutex_trylock(&ths->m_inwait_mutex)!=0 ) { \
41  mrmailbox_log_info(ths->m_mailbox, 0, "Interrupting IDLE..."); \
42  mailstream_interrupt_idle(ths->m_hEtpan->imap_stream); \
43  pthread_mutex_lock(&ths->m_inwait_mutex); /* make sure, mailimap_idle_done() is called - otherwise the other routines do not work */ \
44  } \
45  pthread_mutex_unlock(&ths->m_inwait_mutex); \
46  }
47 
48 static int setup_handle_if_needed__ (mrimap_t*);
49 static void unsetup_handle__ (mrimap_t*);
50 
51 
52 /*******************************************************************************
53  * Tools
54  ******************************************************************************/
55 
56 
57 static int is_error(mrimap_t* ths, int code)
58 {
59  if( code == MAILIMAP_NO_ERROR /*0*/
60  || code == MAILIMAP_NO_ERROR_AUTHENTICATED /*1*/
61  || code == MAILIMAP_NO_ERROR_NON_AUTHENTICATED /*2*/ )
62  {
63  return 0;
64  }
65 
66  if( code == MAILIMAP_ERROR_STREAM /*4*/
67  || code == MAILIMAP_ERROR_PARSE /*5*/ )
68  {
69  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP stream lost; we'll reconnect soon.");
70  ths->m_should_reconnect = 1;
71  }
72 
73  return 1;
74 }
75 
76 
77 /*******************************************************************************
78  * Handle folders
79  ******************************************************************************/
80 
81 
82 static int get_folder_meaning(const mrimap_t* ths, struct mailimap_mbx_list_flags* flags, const char* folder_name, bool force_fallback)
83 {
84  #define MEANING_NORMAL 1
85  #define MEANING_INBOX 2
86  #define MEANING_IGNORE 3
87  #define MEANING_SENT_OBJECTS 4
88 
89  char* lower = NULL;
90  int ret_meaning = MEANING_NORMAL;
91 
92  if( !force_fallback && (ths->m_has_xlist || flags != NULL) )
93  {
94  /* We check for flags if we get some (LIST may also return some, see https://tools.ietf.org/html/rfc6154 )
95  or if m_has_xlist is set. However, we also allow a NULL-pointer for "no flags" if m_has_xlist is true. */
96  if( flags && flags->mbf_oflags )
97  {
98  clistiter* iter2;
99  for( iter2=clist_begin(flags->mbf_oflags); iter2!=NULL; iter2=clist_next(iter2) )
100  {
101  struct mailimap_mbx_list_oflag* oflag = (struct mailimap_mbx_list_oflag*)clist_content(iter2);
102  switch( oflag->of_type )
103  {
104  case MAILIMAP_MBX_LIST_OFLAG_FLAG_EXT:
105  if( strcasecmp(oflag->of_flag_ext, "spam")==0
106  || strcasecmp(oflag->of_flag_ext, "trash")==0
107  || strcasecmp(oflag->of_flag_ext, "drafts")==0
108  || strcasecmp(oflag->of_flag_ext, "junk")==0 )
109  {
110  ret_meaning = MEANING_IGNORE;
111  }
112  else if( strcasecmp(oflag->of_flag_ext, "sent")==0 )
113  {
114  ret_meaning = MEANING_SENT_OBJECTS;
115  }
116  else if( strcasecmp(oflag->of_flag_ext, "inbox")==0 )
117  {
118  ret_meaning = MEANING_INBOX;
119  }
120  break;
121  }
122  }
123  }
124  }
125  else
126  {
127  /* we have no flag list; try some known default names */
128  lower = mr_strlower(folder_name);
129  if( strcmp(lower, "spam") == 0
130  || strcmp(lower, "junk") == 0
131  || strcmp(lower, "indésirables") == 0 /* fr */
132 
133  || strcmp(lower, "trash") == 0
134  || strcmp(lower, "deleted") == 0
135  || strcmp(lower, "deleted items") == 0
136  || strcmp(lower, "papierkorb") == 0 /* de */
137  || strcmp(lower, "corbeille") == 0 /* fr */
138  || strcmp(lower, "papelera") == 0 /* es */
139  || strcmp(lower, "papperskorg") == 0 /* sv */
140 
141  || strcmp(lower, "drafts") == 0
142  || strcmp(lower, "entwürfe") == 0 /* de */
143  || strcmp(lower, "brouillons") == 0 /* fr */
144  || strcmp(lower, "borradores") == 0 /* es */
145  || strcmp(lower, "utkast") == 0 /* sv */
146  )
147  {
148  ret_meaning = MEANING_IGNORE;
149  }
150  else if( strcmp(lower, "inbox") == 0 ) /* the "INBOX" foldername is IMAP-standard */
151  {
152  ret_meaning = MEANING_INBOX;
153  }
154  else if( strcmp(lower, "sent")==0 || strcmp(lower, "sent objects")==0 || strcmp(lower, "gesendet")==0 )
155  {
156  ret_meaning = MEANING_SENT_OBJECTS;
157  }
158  }
159 
160  free(lower);
161  return ret_meaning;
162 }
163 
164 
165 typedef struct mrimapfolder_t
166 {
167  char* m_name_to_select;
168  char* m_name_utf8;
169  int m_meaning;
170 } mrimapfolder_t;
171 
172 
173 static clist* list_folders__(mrimap_t* ths)
174 {
175  clist* imap_list = NULL;
176  clistiter* iter1;
177  clist * ret_list = clist_new();
178  int r, xlist_works = 0;
179 
180  if( ths==NULL || ths->m_hEtpan==NULL ) {
181  goto cleanup;
182  }
183 
184  /* the "*" not only gives us the folders from the main directory, but also all subdirectories; so the resulting foldernames may contain
185  delimiters as "folder/subdir/subsubdir" etc. However, as we do not really use folders, this is just fine (otherwise we'd implement this
186  functinon recursively. */
187  if( ths->m_has_xlist ) {
188  r = mailimap_xlist(ths->m_hEtpan, "", "*", &imap_list);
189  }
190  else {
191  r = mailimap_list(ths->m_hEtpan, "", "*", &imap_list);
192  }
193  if( is_error(ths, r) || imap_list==NULL ) {
194  imap_list = NULL;
195  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot get folder list.");
196  goto cleanup;
197  }
198 
199  for( iter1 = clist_begin(imap_list); iter1 != NULL ; iter1 = clist_next(iter1) )
200  {
201  struct mailimap_mailbox_list* imap_folder = (struct mailimap_mailbox_list*)clist_content(iter1);
202  mrimapfolder_t* ret_folder = calloc(1, sizeof(mrimapfolder_t));
203 
204  if( strcasecmp(imap_folder->mb_name, "INBOX")==0 ) {
205  /* Force upper case INBOX as we also use it directly this way; a unified name is needed as we use the folder name to remember the last uid.
206  Servers may return any case, however, all variants MUST lead to the same INBOX, see RFC 3501 5.1 */
207  ret_folder->m_name_to_select = safe_strdup("INBOX");
208  }
209  else {
210  ret_folder->m_name_to_select = safe_strdup(imap_folder->mb_name);
211  }
212 
213  ret_folder->m_name_utf8 = imap_modified_utf7_to_utf8(imap_folder->mb_name, 0);
214  ret_folder->m_meaning = get_folder_meaning(ths, imap_folder->mb_flag, ret_folder->m_name_utf8, false);
215 
216  if( ret_folder->m_meaning != MEANING_NORMAL ) {
217  xlist_works = 1;
218  }
219 
220  clist_append(ret_list, (void*)ret_folder);
221  }
222 
223  /* at least my own server claims that it support XLIST but does not return folder flags. So, if we did not get a single
224  flag, fall back to the default behaviour */
225  if( !xlist_works ) {
226  for( iter1 = clist_begin(ret_list); iter1 != NULL ; iter1 = clist_next(iter1) )
227  {
228  mrimapfolder_t* ret_folder = (struct mrimapfolder_t*)clist_content(iter1);
229  ret_folder->m_meaning = get_folder_meaning(ths, NULL, ret_folder->m_name_utf8, true);
230  }
231  }
232 
233 cleanup:
234  if( imap_list ) {
235  mailimap_list_result_free(imap_list);
236  }
237  return ret_list;
238 }
239 
240 
241 static void free_folders(clist* folders)
242 {
243  if( folders ) {
244  clistiter* iter1;
245  for( iter1 = clist_begin(folders); iter1 != NULL ; iter1 = clist_next(iter1) ) {
246  mrimapfolder_t* ret_folder = (struct mrimapfolder_t*)clist_content(iter1);
247  free(ret_folder->m_name_to_select);
248  free(ret_folder->m_name_utf8);
249  free(ret_folder);
250  }
251  clist_free(folders);
252  }
253 }
254 
255 
256 static int init_chat_folders__(mrimap_t* ths)
257 {
258  int success = 0;
259  clist* folder_list = NULL;
260  clistiter* iter1;
261  char *normal_folder = NULL, *sent_folder = NULL, *chats_folder = NULL;
262 
263  if( ths==NULL || ths->m_hEtpan==NULL ) {
264  goto cleanup;
265  }
266 
267  if( ths->m_sent_folder && ths->m_sent_folder[0] ) {
268  success = 1;
269  goto cleanup;
270  }
271 
272  free(ths->m_sent_folder);
273  ths->m_sent_folder = NULL;
274 
275  free(ths->m_moveto_folder);
276  ths->m_moveto_folder = NULL;
277 
278  folder_list = list_folders__(ths);
279  for( iter1 = clist_begin(folder_list); iter1 != NULL ; iter1 = clist_next(iter1) ) {
280  mrimapfolder_t* folder = (struct mrimapfolder_t*)clist_content(iter1);
281  if( strcmp(folder->m_name_utf8, MR_CHATS_FOLDER)==0 ) {
282  chats_folder = safe_strdup(folder->m_name_to_select);
283  break;
284  }
285  else if( folder->m_meaning == MEANING_SENT_OBJECTS ) {
286  sent_folder = safe_strdup(folder->m_name_to_select);
287  }
288  else if( folder->m_meaning == MEANING_NORMAL && normal_folder == NULL ) {
289  normal_folder = safe_strdup(folder->m_name_to_select);
290  }
291  }
292 
293  if( chats_folder == NULL && (ths->m_server_flags&MR_NO_MOVE_TO_CHATS)==0 ) {
294  mrmailbox_log_info(ths->m_mailbox, 0, "Creating IMAP-folder \"%s\"...", MR_CHATS_FOLDER);
295  int r = mailimap_create(ths->m_hEtpan, MR_CHATS_FOLDER);
296  if( is_error(ths, r) ) {
297  /* continue on errors, we'll just use a different folder then */
298  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot create IMAP-folder, using default.");
299  }
300  else {
301  chats_folder = safe_strdup(MR_CHATS_FOLDER);
302  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-folder created.");
303  }
304  }
305 
306  /* Subscribe to the created folder. Otherwise, although a top-level folder, if clients use LSUB for listing, the created folder may be hidden.
307  (we could also do this directly after creation, however, we forgot this in versions <v0.1.19 */
308  if( chats_folder && ths->m_get_config_int(ths, "imap.subscribedToChats", 0)==0 ) {
309  mailimap_subscribe(ths->m_hEtpan, chats_folder);
310  ths->m_set_config_int(ths, "imap.subscribedToChats", 1);
311  }
312 
313  if( chats_folder ) {
314  ths->m_moveto_folder = safe_strdup(chats_folder);
315  ths->m_sent_folder = safe_strdup(chats_folder);
316  success = 1;
317  }
318  else if( sent_folder ) {
319  ths->m_sent_folder = safe_strdup(sent_folder);
320  success = 1;
321  }
322  else if( normal_folder ) {
323  ths->m_sent_folder = safe_strdup(normal_folder);
324  success = 1;
325  }
326 
327 cleanup:
328  free_folders(folder_list);
329  free(chats_folder);
330  free(sent_folder);
331  free(normal_folder);
332  return success;
333 }
334 
335 
336 static void forget_folder_selection__(mrimap_t* ths)
337 {
338  ths->m_selected_folder[0] = 0;
339 }
340 
341 
342 static int select_folder__(mrimap_t* ths, const char* folder)
343 {
344  if( ths==NULL || ths->m_hEtpan==NULL ) {
345  return 0;
346  }
347 
348  if( strcmp(ths->m_selected_folder, folder)==0 ) {
349  return 1;
350  }
351 
352  int r = mailimap_select(ths->m_hEtpan, folder);
353  if( is_error(ths, r) || ths->m_hEtpan->imap_selection_info == NULL ) {
354  ths->m_selected_folder[0] = 0;
355  return 0;
356  }
357  else {
358  free(ths->m_selected_folder);
359  ths->m_selected_folder = safe_strdup(folder);
360  return 1;
361  }
362 }
363 
364 
365 /*******************************************************************************
366  * Fetch Messages
367  ******************************************************************************/
368 
369 
370 static uint32_t peek_uid(struct mailimap_msg_att* msg_att)
371 {
372  /* search the UID in a list of attributes returned by a FETCH command */
373  clistiter* iter1;
374  for( iter1=clist_begin(msg_att->att_list); iter1!=NULL; iter1=clist_next(iter1) )
375  {
376  struct mailimap_msg_att_item* item = (struct mailimap_msg_att_item*)clist_content(iter1);
377  if( item )
378  {
379  if( item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC )
380  {
381  if( item->att_data.att_static->att_type == MAILIMAP_MSG_ATT_UID )
382  {
383  return item->att_data.att_static->att_data.att_uid;
384  }
385  }
386  }
387  }
388 
389  return 0;
390 }
391 
392 
393 static int peek_flag_keyword(struct mailimap_msg_att* msg_att, const char* flag_keyword)
394 {
395  /* search $MDNSent in a list of attributes returned by a FETCH command */
396  if( msg_att == NULL || msg_att->att_list==NULL || flag_keyword == NULL ) {
397  return 0;
398  }
399 
400  clistiter *iter1, *iter2;
401  for( iter1=clist_begin(msg_att->att_list); iter1!=NULL; iter1=clist_next(iter1) )
402  {
403  struct mailimap_msg_att_item* item = (struct mailimap_msg_att_item*)clist_content(iter1);
404  if( item )
405  {
406  if( item->att_type == MAILIMAP_MSG_ATT_ITEM_DYNAMIC )
407  {
408  if( item->att_data.att_dyn->att_list /*I've seen NULL here ...*/ )
409  {
410  for( iter2=clist_begin(item->att_data.att_dyn->att_list); iter2!=NULL ; iter2=clist_next(iter2))
411  {
412  struct mailimap_flag_fetch* flag_fetch =(struct mailimap_flag_fetch*) clist_content(iter2);
413  if( flag_fetch && flag_fetch->fl_type==MAILIMAP_FLAG_FETCH_OTHER )
414  {
415  struct mailimap_flag* flag = flag_fetch->fl_flag;
416  if( flag )
417  {
418  if( flag->fl_type == MAILIMAP_FLAG_KEYWORD && flag->fl_data.fl_keyword!=NULL
419  && strcmp(flag->fl_data.fl_keyword, flag_keyword)==0 ) {
420  return 1; /* flag found */
421  }
422  }
423  }
424  }
425  }
426  }
427  }
428  }
429  return 0;
430 }
431 
432 
433 static void peek_body(struct mailimap_msg_att* msg_att, char** p_msg, size_t* p_msg_bytes, uint32_t* flags, int* deleted)
434 {
435  /* search body & Co. in a list of attributes returned by a FETCH command */
436  clistiter *iter1, *iter2;
437  for( iter1=clist_begin(msg_att->att_list); iter1!=NULL; iter1=clist_next(iter1) )
438  {
439  struct mailimap_msg_att_item* item = (struct mailimap_msg_att_item*)clist_content(iter1);
440  if( item )
441  {
442  if( item->att_type == MAILIMAP_MSG_ATT_ITEM_DYNAMIC )
443  {
444  if( item->att_data.att_dyn->att_list /*I've seen NULL here ...*/ )
445  {
446  for( iter2=clist_begin(item->att_data.att_dyn->att_list); iter2!=NULL ; iter2=clist_next(iter2))
447  {
448  struct mailimap_flag_fetch* flag_fetch =(struct mailimap_flag_fetch*) clist_content(iter2);
449  if( flag_fetch && flag_fetch->fl_type==MAILIMAP_FLAG_FETCH_OTHER )
450  {
451  struct mailimap_flag* flag = flag_fetch->fl_flag;
452  if( flag )
453  {
454  if( flag->fl_type == MAILIMAP_FLAG_SEEN ) {
455  *flags |= MR_IMAP_SEEN;
456  }
457  else if( flag->fl_type == MAILIMAP_FLAG_DELETED ) {
458  *deleted = 1;
459  }
460  }
461  }
462  }
463  }
464  }
465  else if( item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC )
466  {
467  if( item->att_data.att_static->att_type == MAILIMAP_MSG_ATT_BODY_SECTION )
468  {
469  *p_msg = item->att_data.att_static->att_data.att_body_section->sec_body_part;
470  *p_msg_bytes = item->att_data.att_static->att_data.att_body_section->sec_length;
471  }
472  }
473  }
474  }
475 }
476 
477 
478 static int fetch_single_msg(mrimap_t* ths, const char* folder, uint32_t server_uid, int block_idle)
479 {
480  /* the function returns:
481  0 the caller should try over again later
482  or 1 if the messages should be treated as received, the caller should not try to read the message again (even if no database entries are returned) */
483  char* msg_content;
484  size_t msg_bytes;
485  int r, retry_later = 0, deleted = 0, handle_locked = 0, idle_blocked = 0;
486  uint32_t flags = 0;
487  clist* fetch_result = NULL;
488  clistiter* cur;
489 
490  if( ths==NULL ) {
491  goto cleanup;
492  }
493 
494  LOCK_HANDLE
495 
496  if( ths->m_hEtpan==NULL ) {
497  goto cleanup;
498  }
499 
500  if( block_idle ) {
501  BLOCK_IDLE
502  INTERRUPT_IDLE
503  setup_handle_if_needed__(ths);
504  forget_folder_selection__(ths);
505  select_folder__(ths, folder); /* if we need to block IDLE, we'll also need to select the folder as it may have changed by IDLE */
506  }
507 
508  {
509  struct mailimap_set* set = mailimap_set_new_single(server_uid);
510  r = mailimap_uid_fetch(ths->m_hEtpan, set, ths->m_fetch_type_body, &fetch_result);
511  mailimap_set_free(set);
512  }
513 
514  if( block_idle ) {
515  UNBLOCK_IDLE
516  }
517 
518  UNLOCK_HANDLE
519 
520  if( is_error(ths, r) || fetch_result == NULL ) {
521  fetch_result = NULL;
522  mrmailbox_log_warning(ths->m_mailbox, 0, "Error #%i on fetching message #%i from folder \"%s\"; retry=%i.", (int)r, (int)server_uid, folder, (int)ths->m_should_reconnect);
523  if( ths->m_should_reconnect ) {
524  retry_later = 1; /* maybe we should also retry on other errors, however, we should check this carefully, as this may result in a dead lock! */
525  }
526  goto cleanup; /* this is an error that should be recovered; the caller should try over later to fetch the message again (if there is no such message, we simply get an empty result) */
527  }
528 
529  if( (cur=clist_begin(fetch_result)) == NULL ) {
530  mrmailbox_log_warning(ths->m_mailbox, 0, "Message #%i does not exist in folder \"%s\".", (int)server_uid, folder);
531  goto cleanup; /* server response is fine, however, there is no such message, do not try to fetch the message again */
532  }
533 
534  struct mailimap_msg_att* msg_att = (struct mailimap_msg_att*)clist_content(cur);
535  peek_body(msg_att, &msg_content, &msg_bytes, &flags, &deleted);
536  if( msg_content == NULL || msg_bytes <= 0 || deleted ) {
537  /* mrmailbox_log_warning(ths->m_mailbox, 0, "Message #%i in folder \"%s\" is empty or deleted.", (int)server_uid, folder); -- this is a quite usual situation, do not print a warning */
538  goto cleanup;
539  }
540 
541  ths->m_receive_imf(ths, msg_content, msg_bytes, folder, server_uid, flags);
542 
543 cleanup:
544  if( block_idle ) {
545  UNBLOCK_IDLE
546  }
547  UNLOCK_HANDLE
548 
549  if( fetch_result ) {
550  mailimap_fetch_list_free(fetch_result);
551  }
552  return retry_later? 0 : 1;
553 }
554 
555 
556 static int fetch_from_single_folder(mrimap_t* ths, const char* folder, uint32_t uidvalidity)
557 {
558  int r, handle_locked = 0, log_summary = 1;
559  clist* fetch_result = NULL;
560  uint32_t out_largetst_uid = 0;
561  size_t read_cnt = 0, read_errors = 0;
562  clistiter* cur;
563 
564  uint32_t lastuid = 0; /* The last uid fetched, we fetch from lastuid+1. If 0, we get some of the newest ones. */
565  char* lastuid_config_key = NULL;
566 
567  if( ths==NULL ) {
568  goto cleanup;
569  }
570 
571  LOCK_HANDLE
572 
573  if( ths->m_hEtpan==NULL ) {
574  mrmailbox_log_info(ths->m_mailbox, 0, "Cannot fetch from \"%s\" - no connected.", folder);
575  goto cleanup;
576  }
577 
578  if( uidvalidity )
579  {
580  lastuid_config_key = mr_mprintf("imap.lastuid.%lu.%s",
581  (unsigned long)uidvalidity, folder); /* RFC3501: UID are unique and should grow only, for mailbox recreation etc. UIDVALIDITY changes. */
582  lastuid = ths->m_get_config_int(ths, lastuid_config_key, 0);
583 
584  if( lastuid > 0 ) {
585  struct mailimap_set* set = mailimap_set_new_interval(lastuid+1, 0);
586  r = mailimap_uid_fetch(ths->m_hEtpan, set, ths->m_fetch_type_uid, &fetch_result); /* execute UID FETCH from:to command, result includes the given UIDs */
587  mailimap_set_free(set);
588  }
589  else {
590  /* fall back to init behaviour below */
591  free(lastuid_config_key);
592  lastuid_config_key = NULL;
593  lastuid = 0;
594  }
595 
596  mrmailbox_log_info(ths->m_mailbox, 0, "%s=%lu", lastuid_config_key, (unsigned long)lastuid);
597  }
598 
599  if( lastuid == 0 )
600  {
601  if( select_folder__(ths, folder)==0 ) {
602  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot select folder \"%s\".", folder);
603  log_summary = 0;
604  goto cleanup;
605  }
606 
607  lastuid_config_key = mr_mprintf("imap.lastuid.%lu.%s",
608  (unsigned long)ths->m_hEtpan->imap_selection_info->sel_uidvalidity, folder); /* RFC3501: UID are unique and should grow only, for mailbox recreation etc. UIDVALIDITY changes. */
609  lastuid = ths->m_get_config_int(ths, lastuid_config_key, 0);
610 
611  mrmailbox_log_info(ths->m_mailbox, 0, "%s=%lu (validity read from folder)", lastuid_config_key, (unsigned long)lastuid);
612 
613  if( lastuid == 0 ) {
614  if( ths->m_hEtpan->imap_selection_info->sel_uidnext != 0 ) {
615  lastuid = ths->m_hEtpan->imap_selection_info->sel_uidnext - 1; /* this is not always exact, however, as we only use this as "range start", this is no real problem */
616  ths->m_set_config_int(ths, lastuid_config_key, lastuid); /* write back the UID as otherwise we'll never receive new messages as UIDNEXT grows as messages come in */
617  }
618  }
619 
620  if( lastuid > 0 )
621  {
622  /* Get messages with an ID larger than the one we got last time */
623 
624  /* check if the predicted "next uid on insert" is equal to the one we start for fetching
625  -> no new messages (this check only works for the folder with the last message, however, most times this is INBOX - and the check is cheap)
626  --> unfortunately, this check does not work with our cached select!
627  if( ths->m_hEtpan->imap_selection_info->sel_uidnext == lastuid+1 ) {
628  goto cleanup;
629  }
630  */
631 
632  struct mailimap_set* set = mailimap_set_new_interval(lastuid+1, 0);
633  r = mailimap_uid_fetch(ths->m_hEtpan, set, ths->m_fetch_type_uid, &fetch_result); /* execute UID FETCH from:to command, result includes the given UIDs */
634  mailimap_set_free(set);
635  }
636  else
637  {
638  /* fetch the newest message to get the UID */
639  struct mailimap_set* set = mailimap_set_new_single(0); // 0=*=the _largest_ index (results in `FETCH * (UID)` to get the latest message)
640  r = mailimap_fetch(ths->m_hEtpan, set, ths->m_fetch_type_uid, &fetch_result); /* execute FETCH from:to command, result includes the given index */
641  mailimap_set_free(set);
642 
643  #if 0
644  /* fetch the last 200 messages by one-based-index.
645  This implementation seems to make problems if we cannot get the count for any reasons ... see mailbox.org ... */
646  int32_t i_first = 1, i_last = 200; /* if we cannot get the count, we start with the oldest messages; normally, this should not happen */
647  if( ths->m_hEtpan->imap_selection_info->sel_has_exists ) {
648  i_last = ths->m_hEtpan->imap_selection_info->sel_exists;
649  i_first = MR_MAX(i_last-200, 1);
650  }
651 
652  struct mailimap_set* set = mailimap_set_new_interval(i_first, i_last);
653  r = mailimap_fetch(ths->m_hEtpan, set, ths->m_fetch_type_uid, &fetch_result); /* execute FETCH from:to command, result includes the given index */
654  mailimap_set_free(set);
655  #endif
656  }
657  }
658 
659  UNLOCK_HANDLE
660 
661  if( is_error(ths, r) || fetch_result == NULL )
662  {
663  fetch_result = NULL;
664  if( r == MAILIMAP_ERROR_PROTOCOL ) {
665  mrmailbox_log_info(ths->m_mailbox, 0, "Folder \"%s\" is empty", folder);
666  goto cleanup; /* the folder is simply empty, this is no error */
667  }
668  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot fetch message list from folder \"%s\".", folder);
669  log_summary = 0;
670  goto cleanup;
671  }
672 
673  /* go through all mails in folder (this is typically _fast_ as we already have the whole list) */
674  for( cur = clist_begin(fetch_result); cur != NULL ; cur = clist_next(cur) )
675  {
676  struct mailimap_msg_att* msg_att = (struct mailimap_msg_att*)clist_content(cur); /* mailimap_msg_att is a list of attributes: list is a list of message attributes */
677  uint32_t cur_uid = peek_uid(msg_att);
678  if( cur_uid && (lastuid==0 || cur_uid>lastuid) ) /* normally, the "cur_uid>lastuid" is not needed, however, some server return some smaller IDs under some curcumstances. Mailcore2 does the same check, see see "if (uid < fromUID) {..}"@IMAPSession::fetchMessageNumberUIDMapping()@MCIMAPSession.cpp */
679  {
680  read_cnt++;
681  if( fetch_single_msg(ths, folder, cur_uid, 0) == 0 ) {
682  read_errors++;
683  }
684  else if( cur_uid > out_largetst_uid ) {
685  out_largetst_uid = cur_uid;
686  }
687  }
688  }
689 
690  if( !read_errors && out_largetst_uid > 0 ) {
691  ths->m_set_config_int(ths, lastuid_config_key, out_largetst_uid);
692  }
693 
694  /* done */
695 cleanup:
696  UNLOCK_HANDLE
697 
698  if( log_summary )
699  {
700  char* temp = mr_mprintf("%i mails read from \"%s\" with %i errors.", (int)read_cnt, folder, (int)read_errors);
701  if( read_errors ) {
702  mrmailbox_log_warning(ths->m_mailbox, 0, temp);
703  }
704  else {
705  mrmailbox_log_info(ths->m_mailbox, 0, temp);
706  }
707  free(temp);
708  }
709 
710  if( fetch_result ) {
711  mailimap_fetch_list_free(fetch_result);
712  }
713 
714  if( lastuid_config_key ) {
715  free(lastuid_config_key);
716  }
717 
718  return read_cnt;
719 }
720 
721 
722 static int fetch_from_all_folders(mrimap_t* ths)
723 {
724  int handle_locked = 0;
725  clist* folder_list = NULL;
726  clistiter* cur;
727  int total_cnt = 0;
728 
729  mrmailbox_log_info(ths->m_mailbox, 0, "Fetching from all folders.");
730 
731  LOCK_HANDLE
732  folder_list = list_folders__(ths);
733  UNLOCK_HANDLE
734 
735  /* first, read the INBOX, this looks much better on the initial load as the INBOX
736  has the most recent mails. Moreover, this is for speed reasons, as the other folders only have few new messages. */
737  for( cur = clist_begin(folder_list); cur != NULL ; cur = clist_next(cur) )
738  {
739  mrimapfolder_t* folder = (mrimapfolder_t*)clist_content(cur);
740  if( folder->m_meaning == MEANING_INBOX ) {
741  total_cnt += fetch_from_single_folder(ths, folder->m_name_to_select, 0);
742  }
743  }
744 
745  for( cur = clist_begin(folder_list); cur != NULL ; cur = clist_next(cur) )
746  {
747  mrimapfolder_t* folder = (mrimapfolder_t*)clist_content(cur);
748  if( folder->m_meaning == MEANING_IGNORE ) {
749  mrmailbox_log_info(ths->m_mailbox, 0, "Folder \"%s\" ignored.", folder->m_name_utf8);
750  }
751  else if( folder->m_meaning != MEANING_INBOX ) {
752  total_cnt += fetch_from_single_folder(ths, folder->m_name_to_select, 0);
753  }
754  }
755 
756  free_folders(folder_list);
757 
758  return total_cnt;
759 }
760 
761 
762 /*******************************************************************************
763  * Watch thread
764  ******************************************************************************/
765 
766 
767 static void* watch_thread_entry_point(void* entry_arg)
768 {
769  mrimap_t* ths = (mrimap_t*)entry_arg;
770  mrosnative_setup_thread(ths->m_mailbox); /* must be very first */
771 
772  int handle_locked = 0, idle_blocked = 0, force_sleep = 0, do_fetch = 0;
773  #define SLEEP_ON_ERROR_SECONDS 10
774  #define SLEEP_ON_INTERRUPT_SECONDS 2 /* let the job thread a little bit time before we IDLE again, otherweise there will be many idle-interrupt sequences */
775  #define IDLE_DELAY_SECONDS (28*60) /* 28 minutes is a typical maximum, most servers do not allow more. if the delay is reached, we also check _all_ folders. */
776  #define FULL_FETCH_EVERY_SECONDS (27*60) /* force a full fetch every 27 minute (typically together the IDLE delay break) */
777 
778  time_t last_fullread_time = 0;
779 
780  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-watch-thread started.");
781 
782  if( ths->m_can_idle )
783  {
784  /* watch using IDLE
785  **********************************************************************/
786 
787  int r, r2;
788  uint32_t uidvalidity;
789 
790  fetch_from_all_folders(ths); /* the initial fetch from all folders is needed as this will init the folder UIDs (see fetch_from_single_folder() if lastuid is unset) */
791  last_fullread_time = time(NULL);
792 
793  while( 1 )
794  {
795  if( ths->m_watch_do_exit ) {
796  goto exit_;
797  }
798 
799  BLOCK_IDLE /* must be done before LOCK_HANDLE; this allows other threads to block IDLE */
800  LOCK_HANDLE
801 
802  do_fetch = 0;
803  force_sleep = SLEEP_ON_ERROR_SECONDS;
804  uidvalidity = 0;
805 
806  setup_handle_if_needed__(ths);
807  if( ths->m_idle_set_up==0 && ths->m_hEtpan && ths->m_hEtpan->imap_stream ) {
808  if( time(NULL)-last_fullread_time > FULL_FETCH_EVERY_SECONDS ) {
809  /* we go here only if we get MAILSTREAM_IDLE_ERROR or MAILSTREAM_IDLE_CANCELLED instead or a proper timeout */
810  UNLOCK_HANDLE
811  UNBLOCK_IDLE
812  fetch_from_all_folders(ths);
813  BLOCK_IDLE
814  LOCK_HANDLE
815  last_fullread_time = time(NULL);
816  }
817  mailstream_setup_idle(ths->m_hEtpan->imap_stream);
818  ths->m_idle_set_up = 1;
819  }
820 
821  if( select_folder__(ths, "INBOX") )
822  {
823  uidvalidity = ths->m_hEtpan->imap_selection_info->sel_uidvalidity;
824  r = mailimap_idle(ths->m_hEtpan);
825  if( !is_error(ths, r) )
826  {
827  mrmailbox_log_info(ths->m_mailbox, 0, "IDLE start...");
828 
829  ths->m_enter_watch_wait_time = time(NULL);
830 
831  UNLOCK_HANDLE
832  UNBLOCK_IDLE
833 
834  pthread_mutex_lock(&ths->m_inwait_mutex);
835  r = 0; r2 = 0;
836  if( ths->m_hEtpan ) {
837  r = mailstream_wait_idle(ths->m_hEtpan->imap_stream, IDLE_DELAY_SECONDS);
838  r2 = mailimap_idle_done(ths->m_hEtpan); /* it's okay to use the handle without locking as we're inwait */
839  }
840  pthread_mutex_unlock(&ths->m_inwait_mutex);
841  force_sleep = 0;
842 
843  if( r == MAILSTREAM_IDLE_ERROR /*0*/ || r==MAILSTREAM_IDLE_CANCELLED /*4*/ ) {
844  mrmailbox_log_info(ths->m_mailbox, 0, "IDLE wait cancelled, r=%i, r2=%i; we'll reconnect soon.", (int)r, (int)r2);
845  force_sleep = SLEEP_ON_ERROR_SECONDS;
846  ths->m_should_reconnect = 1;
847  }
848  else if( r == MAILSTREAM_IDLE_INTERRUPTED /*1*/ ) {
849  mrmailbox_log_info(ths->m_mailbox, 0, "IDLE interrupted.");
850  force_sleep = SLEEP_ON_INTERRUPT_SECONDS;
851  }
852  else if( r == MAILSTREAM_IDLE_HASDATA /*2*/ ) {
853  mrmailbox_log_info(ths->m_mailbox, 0, "IDLE has data.");
854  do_fetch = 1;
855  }
856  else if( r == MAILSTREAM_IDLE_TIMEOUT /*3*/ ) {
857  mrmailbox_log_info(ths->m_mailbox, 0, "IDLE timeout.");
858  do_fetch = 1;
859  }
860 
861  if( is_error(ths, r2) ) {
862  do_fetch = 0;
863  }
864 
865  if( ths->m_watch_do_exit ) { /* check after is_error() to allow reconnections on errors */
866  goto exit_;
867  }
868 
869  BLOCK_IDLE
870  LOCK_HANDLE
871 
872  ths->m_enter_watch_wait_time = 0;
873  }
874  }
875 
876  UNLOCK_HANDLE
877  UNBLOCK_IDLE
878 
879  if( do_fetch == 1 && time(NULL)-last_fullread_time > FULL_FETCH_EVERY_SECONDS ) {
880  do_fetch = 2;
881  }
882 
883  if( do_fetch == 1 ) {
884  fetch_from_single_folder(ths, "INBOX", uidvalidity);
885  }
886  else if( do_fetch == 2 ) {
887  fetch_from_all_folders(ths);
888  last_fullread_time = time(NULL);
889  }
890  else if( force_sleep ) {
891  sleep(force_sleep);
892  }
893  }
894  }
895  else
896  {
897  /* watch using POLL
898  **********************************************************************/
899 
900  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-watch-thread will poll for messages.");
901  time_t last_message_time=time(NULL), now, seconds_to_wait;
902  while( 1 )
903  {
904  /* get the latest messages */
905  now = time(NULL);
906 
907  do_fetch = 1;
908  if( now-last_fullread_time > FULL_FETCH_EVERY_SECONDS ) {
909  do_fetch = 2;
910  }
911 
912  LOCK_HANDLE
913  setup_handle_if_needed__(ths);
914  forget_folder_selection__(ths); /* seems to be needed - otherwise, we'll get a new message only every _twice_ polls. WTF? */
915  UNLOCK_HANDLE
916 
917  if( do_fetch == 1 ) {
918  if( fetch_from_single_folder(ths, "INBOX", 0) > 0 ) {
919  last_message_time = now;
920  }
921  }
922  else if( do_fetch == 2 ) {
923  if( fetch_from_all_folders(ths) > 0 ) {
924  last_message_time = now;
925  }
926  last_fullread_time = now;
927  }
928 
929  /* calculate the wait time: every 10 seconds in the first 2 minutes after a new message, after that growing up to 5 minutes */
930  if( now-last_message_time < 2*60 ) {
931  seconds_to_wait = 10;
932  }
933  else {
934  seconds_to_wait = (now-last_message_time)/6;
935  if( seconds_to_wait > 5*60 ) {
936  seconds_to_wait = 5*60;
937  }
938  }
939 
940  #ifdef __APPLE__
941  seconds_to_wait = 10; // HACK to force iOS not to work IMAP-IDLE which does not work for now, see also (*)
942  #endif
943 
944  /* wait */
945  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-watch-thread waits %i seconds.", (int)seconds_to_wait);
946  pthread_mutex_lock(&ths->m_watch_condmutex);
947 
948  if( ths->m_watch_condflag == 0 ) {
949  struct timespec timeToWait;
950  timeToWait.tv_sec = time(NULL)+seconds_to_wait;
951  timeToWait.tv_nsec = 0;
952 
953  LOCK_HANDLE
954  ths->m_enter_watch_wait_time = time(NULL);
955  UNLOCK_HANDLE
956 
957  pthread_cond_timedwait(&ths->m_watch_cond, &ths->m_watch_condmutex, &timeToWait); /* unlock mutex -> wait -> lock mutex */
958 
959  LOCK_HANDLE
960  ths->m_enter_watch_wait_time = 0;
961  UNLOCK_HANDLE
962  }
963  ths->m_watch_condflag = 0;
964 
965  if( ths->m_watch_do_exit ) {
966  pthread_mutex_unlock(&ths->m_watch_condmutex);
967  goto exit_;
968  }
969 
970  pthread_mutex_unlock(&ths->m_watch_condmutex);
971  }
972  }
973 
974 exit_:
975  ths->m_enter_watch_wait_time = 0;
976 
977  UNLOCK_HANDLE
978  UNBLOCK_IDLE
979 
980  mrosnative_unsetup_thread(ths->m_mailbox); /* must be very last */
981  return NULL;
982 }
983 
984 
985 static void* heartbeat_thread_entry_point(void* entry_arg)
986 {
987  mrimap_t* ths = (mrimap_t*)entry_arg;
988 
989  mrosnative_setup_thread(ths->m_mailbox); /* must be very first */
990 
991  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-heartbeat-thread started.");
992 
993  while( 1 )
994  {
995  pthread_mutex_lock(&ths->m_heartbeat_condmutex);
996  struct timespec timeToWait;
997  timeToWait.tv_sec = time(NULL) + 50;
998  timeToWait.tv_nsec = 0;
999  pthread_cond_timedwait(&ths->m_heartbeat_cond, &ths->m_heartbeat_condmutex, &timeToWait);
1000  pthread_mutex_unlock(&ths->m_heartbeat_condmutex);
1001  if( ths->m_watch_do_exit ) {
1002  break;
1003  }
1004 
1005  /* After waiting 50 seconds, call mrimap_heartbeat().
1006  As pthread_cond_timedwait() eg. on Android does not always wake up in time when the device sleeps,
1007  you may want to call mrimap_heartbeat() (resp. mrmailbox_heartbeat()) from an additional, IDLE-safe, timer. */
1008  //mrmailbox_log_info(ths->m_mailbox, 0, "<3 IMAP");
1009  mrimap_heartbeat(ths);
1010  }
1011 
1012  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-heartbeat-thread ended.");
1013 
1014  mrosnative_unsetup_thread(ths->m_mailbox); /* must be very last */
1015  return NULL;
1016 }
1017 
1018 
1019 void mrimap_heartbeat(mrimap_t* ths)
1020 {
1021  /* the function */
1022  int handle_locked = 0, idle_blocked = 0;
1023 
1024  if( ths == NULL ) {
1025  return;
1026  }
1027 
1028  LOCK_HANDLE
1029 
1030  if( ths->m_hEtpan == NULL || ths->m_should_reconnect == 1 ) {
1031  goto cleanup;
1032  }
1033 
1034  if( ths->m_enter_watch_wait_time != 0
1035  && time(NULL)-ths->m_enter_watch_wait_time > (IDLE_DELAY_SECONDS+60) )
1036  {
1037  /* force reconnect if the IDLE timeout does not arrive */
1038  mrmailbox_log_info(ths->m_mailbox, 0, "Reconnect forced from the heartbeat thread.");
1039  ths->m_should_reconnect = 1;
1040  ths->m_enter_watch_wait_time = 0;
1041  if( ths->m_can_idle )
1042  {
1043  /* the handle must be LOCKED when calling BLOCK_IDLE */
1044  BLOCK_IDLE
1045  INTERRUPT_IDLE
1046  UNBLOCK_IDLE
1047  }
1048  else
1049  {
1050  UNLOCK_HANDLE
1051 
1052  pthread_mutex_lock(&ths->m_watch_condmutex);
1053  ths->m_watch_condflag = 1;
1054  pthread_cond_signal(&ths->m_watch_cond);
1055  pthread_mutex_unlock(&ths->m_watch_condmutex);
1056  }
1057  }
1058 
1059 cleanup:
1060  UNLOCK_HANDLE
1061 }
1062 
1063 
1064 /*******************************************************************************
1065  * Setup handle
1066  ******************************************************************************/
1067 
1068 
1069 static int setup_handle_if_needed__(mrimap_t* ths)
1070 {
1071  int r, success = 0;
1072 
1073  if( ths==NULL ) {
1074  goto cleanup;
1075  }
1076 
1077  if( ths->m_should_reconnect ) {
1078  unsetup_handle__(ths);
1079  }
1080 
1081  if( ths->m_hEtpan ) {
1082  success = 1;
1083  goto cleanup;
1084  }
1085 
1086  if( ths->m_mailbox->m_cb(ths->m_mailbox, MR_EVENT_IS_ONLINE, 0, 0)!=1 ) {
1087  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, MR_ERR_NONETWORK, NULL);
1088  goto cleanup;
1089  }
1090 
1091  ths->m_hEtpan = mailimap_new(0, NULL);
1092 
1093  mailimap_set_timeout(ths->m_hEtpan, 30); /* 30 second until actions are aborted, this is also used in mailcore2 */
1094 
1095  if( ths->m_server_flags&(MR_IMAP_SOCKET_STARTTLS|MR_IMAP_SOCKET_PLAIN) )
1096  {
1097  mrmailbox_log_info(ths->m_mailbox, 0, "Connecting to IMAP-server \"%s:%i\"...", ths->m_imap_server, (int)ths->m_imap_port);
1098  r = mailimap_socket_connect(ths->m_hEtpan, ths->m_imap_server, ths->m_imap_port);
1099  if( is_error(ths, r) ) {
1100  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "Could not connect to IMAP-server \"%s:%i\". (Error #%i)", ths->m_imap_server, (int)ths->m_imap_port, (int)r);
1101  goto cleanup;
1102  }
1103 
1104  if( ths->m_server_flags&MR_IMAP_SOCKET_STARTTLS )
1105  {
1106  mrmailbox_log_info(ths->m_mailbox, 0, "Switching to IMAP-STARTTLS.", ths->m_imap_server, (int)ths->m_imap_port);
1107  r = mailimap_socket_starttls(ths->m_hEtpan);
1108  if( is_error(ths, r) ) {
1109  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "Could not connect to IMAP-server \"%s:%i\" using STARTLS. (Error #%i)", ths->m_imap_server, (int)ths->m_imap_port, (int)r);
1110  goto cleanup;
1111  }
1112  }
1113  }
1114  else
1115  {
1116  mrmailbox_log_info(ths->m_mailbox, 0, "Connecting to IMAP-server \"%s:%i\" via SSL...", ths->m_imap_server, (int)ths->m_imap_port);
1117  r = mailimap_ssl_connect(ths->m_hEtpan, ths->m_imap_server, ths->m_imap_port);
1118  if( is_error(ths, r) ) {
1119  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "Could not connect to IMAP-server \"%s:%i\" using SSL. (Error #%i)", ths->m_imap_server, (int)ths->m_imap_port, (int)r);
1120  goto cleanup;
1121  }
1122  }
1123  mrmailbox_log_info(ths->m_mailbox, 0, "Connection to IMAP-server ok.");
1124 
1125  mrmailbox_log_info(ths->m_mailbox, 0, "Login to IMAP-server as \"%s\"...", ths->m_imap_user);
1126 
1127  /* TODO: There are more authorisation types, see mailcore2/MCIMAPSession.cpp, however, I'm not sure of they are really all needed */
1128  /*if( ths->m_server_flags&MR_AUTH_XOAUTH2 )
1129  {
1130  //TODO: Support XOAUTH2, we "just" need to get the token someway. If we do so, there is no more need for the user to enable
1131  //https://www.google.com/settings/security/lesssecureapps - however, maybe this is also not needed if the user had enabled 2-factor-authorisation.
1132  if (mOAuth2Token == NULL) {
1133  r = MAILIMAP_ERROR_STREAM;
1134  }
1135  else {
1136  r = mailimap_oauth2_authenticate(ths->m_hEtpan, ths->m_imap_use, mOAuth2Token);
1137  }
1138  }
1139  else*/
1140  {
1141  /* MR_AUTH_NORMAL or no auth flag set */
1142  r = mailimap_login(ths->m_hEtpan, ths->m_imap_user, ths->m_imap_pw);
1143  }
1144 
1145  if( is_error(ths, r) ) {
1146  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "Could not login: %s (Error #%i)", ths->m_hEtpan->imap_response? ths->m_hEtpan->imap_response : "Unknown error.", (int)r);
1147  goto cleanup;
1148  }
1149 
1150  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-Login ok.");
1151 
1152  success = 1;
1153 
1154 cleanup:
1155  if( success == 0 ) {
1156  unsetup_handle__(ths);
1157  }
1158 
1159  ths->m_should_reconnect = 0;
1160  return success;
1161 }
1162 
1163 
1164 static void unsetup_handle__(mrimap_t* ths)
1165 {
1166  if( ths==NULL ) {
1167  return;
1168  }
1169 
1170  if( ths->m_hEtpan )
1171  {
1172  mrmailbox_log_info(ths->m_mailbox, 0, "Disconnecting...");
1173 
1174  if( ths->m_idle_set_up ) {
1175  mailstream_unsetup_idle(ths->m_hEtpan->imap_stream);
1176  ths->m_idle_set_up = 0;
1177  }
1178 
1179  if( ths->m_hEtpan->imap_stream != NULL ) {
1180  mailstream_close(ths->m_hEtpan->imap_stream); /* not sure, if this is really needed, however, mailcore2 does the same */
1181  ths->m_hEtpan->imap_stream = NULL;
1182  }
1183 
1184  mailimap_free(ths->m_hEtpan);
1185  ths->m_hEtpan = NULL;
1186 
1187  mrmailbox_log_info(ths->m_mailbox, 0, "Disconnect done.");
1188  }
1189 
1190  ths->m_selected_folder[0] = 0;
1191 
1192  /* we leave m_sent_folder set; normally this does not change in a normal reconnect; we'll update this folder if we get errors */
1193 }
1194 
1195 
1196 /*******************************************************************************
1197  * Connect/Disconnect
1198  ******************************************************************************/
1199 
1200 
1201 int mrimap_connect(mrimap_t* ths, const mrloginparam_t* lp)
1202 {
1203  int success = 0, handle_locked = 0;
1204 
1205  if( ths==NULL || lp==NULL || lp->m_mail_server==NULL || lp->m_mail_user==NULL || lp->m_mail_pw==NULL ) {
1206  return 0;
1207  }
1208 
1209  LOCK_HANDLE
1210 
1211  if( ths->m_connected ) {
1212  success = 1;
1213  goto cleanup;
1214  }
1215 
1216  free(ths->m_imap_server); ths->m_imap_server = safe_strdup(lp->m_mail_server);
1217  ths->m_imap_port = lp->m_mail_port;
1218  free(ths->m_imap_user); ths->m_imap_user = safe_strdup(lp->m_mail_user);
1219  free(ths->m_imap_pw); ths->m_imap_pw = safe_strdup(lp->m_mail_pw);
1220  ths->m_server_flags = lp->m_server_flags;
1221 
1222  if( !setup_handle_if_needed__(ths) ) {
1223  goto cleanup;
1224  }
1225 
1226  ths->m_connected = 1;
1227 
1228  /* we set the following flags here and not in setup_handle_if_needed__() as they must not change during connection */
1229  ths->m_can_idle = mailimap_has_idle(ths->m_hEtpan);
1230  ths->m_has_xlist = mailimap_has_xlist(ths->m_hEtpan);
1231 
1232  #ifdef __APPLE__
1233  ths->m_can_idle = 0; // HACK to force iOS not to work IMAP-IDLE which does not work for now, see also (*)
1234  #endif
1235 
1236 
1237  if( ths->m_hEtpan->imap_connection_info && ths->m_hEtpan->imap_connection_info->imap_capability ) {
1238  /* just log the whole capabilities list (the mailimap_has_*() function also use this list, so this is a good overview on problems) */
1239  mrstrbuilder_t capinfostr;
1240  mrstrbuilder_init(&capinfostr);
1241  clist* list = ths->m_hEtpan->imap_connection_info->imap_capability->cap_list;
1242  if( list ) {
1243  clistiter* cur;
1244  for(cur = clist_begin(list) ; cur != NULL ; cur = clist_next(cur)) {
1245  struct mailimap_capability * cap = clist_content(cur);
1246  if( cap && cap->cap_type == MAILIMAP_CAPABILITY_NAME ) {
1247  mrstrbuilder_cat(&capinfostr, " ");
1248  mrstrbuilder_cat(&capinfostr, cap->cap_data.cap_name);
1249  }
1250  }
1251  }
1252  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-Capabilities:%s", capinfostr.m_buf);
1253  free(capinfostr.m_buf);
1254  }
1255 
1256  mrmailbox_log_info(ths->m_mailbox, 0, "Starting IMAP-watch-thread...");
1257  ths->m_watch_do_exit = 0;
1258 
1259  UNLOCK_HANDLE
1260 
1261  pthread_create(&ths->m_watch_thread, NULL, watch_thread_entry_point, ths);
1262  pthread_create(&ths->m_heartbeat_thread, NULL, heartbeat_thread_entry_point, ths);
1263 
1264  success = 1;
1265 
1266 cleanup:
1267  if( success == 0 ) {
1268  unsetup_handle__(ths);
1269  }
1270  UNLOCK_HANDLE
1271  return success;
1272 }
1273 
1274 
1275 void mrimap_disconnect(mrimap_t* ths)
1276 {
1277  int handle_locked = 0, connected;
1278 
1279  if( ths==NULL ) {
1280  return;
1281  }
1282 
1283  LOCK_HANDLE
1284  connected = (ths->m_hEtpan && ths->m_connected);
1285  UNLOCK_HANDLE
1286 
1287  if( connected )
1288  {
1289  mrmailbox_log_info(ths->m_mailbox, 0, "Stopping IMAP-watch-thread...");
1290 
1291  /* prepare for exit */
1292  if( ths->m_can_idle && ths->m_hEtpan->imap_stream )
1293  {
1294  ths->m_watch_do_exit = 1;
1295 
1296  LOCK_HANDLE
1297  mrmailbox_log_info(ths->m_mailbox, 0, "Interrupting IDLE for disconnecting...");
1298  mailstream_interrupt_idle(ths->m_hEtpan->imap_stream);
1299  UNLOCK_HANDLE
1300  }
1301  else
1302  {
1303  pthread_mutex_lock(&ths->m_watch_condmutex);
1304  ths->m_watch_condflag = 1;
1305  ths->m_watch_do_exit = 1;
1306  pthread_cond_signal(&ths->m_watch_cond);
1307  pthread_mutex_unlock(&ths->m_watch_condmutex);
1308  }
1309 
1310  pthread_mutex_lock(&ths->m_heartbeat_condmutex);
1311  pthread_cond_signal(&ths->m_heartbeat_cond);
1312  pthread_mutex_unlock(&ths->m_heartbeat_condmutex);
1313 
1314  /* wait for the threads to terminate */
1315  pthread_join(ths->m_watch_thread, NULL);
1316  pthread_join(ths->m_heartbeat_thread, NULL);
1317 
1318  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-watch-thread stopped.");
1319 
1320  if( ths->m_restore_thread_created )
1321  {
1322  mrmailbox_log_info(ths->m_mailbox, 0, "Stopping IMAP-restore-thread...");
1323  ths->m_restore_do_exit = 1;
1324  pthread_join(ths->m_restore_thread, NULL);
1325  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-restore-thread stopped.");
1326  }
1327 
1328  LOCK_HANDLE
1329  unsetup_handle__(ths);
1330  ths->m_can_idle = 0;
1331  ths->m_has_xlist = 0;
1332  ths->m_connected = 0;
1333  UNLOCK_HANDLE
1334  }
1335 }
1336 
1337 
1338 int mrimap_is_connected(mrimap_t* ths)
1339 {
1340  return (ths && ths->m_connected); /* we do not use a LOCK - otherwise, the check may take seconds and is not sufficient for some GUI state updates. */
1341 }
1342 
1343 
1344 /*******************************************************************************
1345  * Restoring
1346  ******************************************************************************/
1347 
1348 
1349 static void* restore_thread_entry_point(void* entry_arg)
1350 {
1351  mrimap_t* ths = (mrimap_t*)entry_arg;
1352  mrosnative_setup_thread(ths->m_mailbox); /* must be very first */
1353 
1354  int r, handle_locked = 0, idle_blocked = 0;
1355  clist *folder_list = NULL, *fetch_result = NULL;
1356  clistiter *folder_iter, *fetch_iter;
1357  #define CHECK_EXIT if( ths->m_restore_do_exit ) { goto exit_; }
1358 
1359  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-restore-thread started.");
1360 
1361  LOCK_HANDLE
1362  BLOCK_IDLE
1363  INTERRUPT_IDLE
1364  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-restore-thread gets folders.");
1365  if( !setup_handle_if_needed__(ths)
1366  || (folder_list=list_folders__(ths))==NULL ) {
1367  goto exit_;
1368  }
1369  UNBLOCK_IDLE
1370  UNLOCK_HANDLE
1371 
1372  for( folder_iter = clist_begin(folder_list); folder_iter != NULL ; folder_iter = clist_next(folder_iter) )
1373  {
1374  mrimapfolder_t* folder = (mrimapfolder_t*)clist_content(folder_iter);
1375 
1376  CHECK_EXIT
1377 
1378  LOCK_HANDLE
1379  BLOCK_IDLE
1380  INTERRUPT_IDLE
1381  setup_handle_if_needed__(ths);
1382  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-restore-thread gets messages in \"%s\".", folder->m_name_utf8);
1383  if( select_folder__(ths, folder->m_name_to_select)
1384  && ths->m_hEtpan->imap_selection_info->sel_has_exists )
1385  {
1386  /* fetch the last 200 messages by one-based-index. TODO: we should regard the given timespan somehow */
1387  int32_t i_last = ths->m_hEtpan->imap_selection_info->sel_exists;
1388  int32_t i_first = MR_MAX(i_last-200, 1);
1389 
1390  struct mailimap_set* set = mailimap_set_new_interval(i_first, i_last);
1391  r = mailimap_fetch(ths->m_hEtpan, set, ths->m_fetch_type_uid, &fetch_result); /* execute FETCH from:to command, result includes the given index */
1392  mailimap_set_free(set);
1393  }
1394  UNBLOCK_IDLE
1395  UNLOCK_HANDLE
1396 
1397  if( !is_error(ths, r) && fetch_result != NULL )
1398  {
1399  for( fetch_iter = clist_begin(fetch_result); fetch_iter != NULL ; fetch_iter = clist_next(fetch_iter) )
1400  {
1401  CHECK_EXIT
1402 
1403  struct mailimap_msg_att* msg_att = (struct mailimap_msg_att*)clist_content(fetch_iter); /* mailimap_msg_att is a list of attributes: list is a list of message attributes */
1404  uint32_t cur_uid = peek_uid(msg_att);
1405  if( cur_uid )
1406  {
1407  fetch_single_msg(ths, folder->m_name_to_select, cur_uid, 1);
1408  }
1409  }
1410 
1411  mailimap_fetch_list_free(fetch_result);
1412  fetch_result = NULL;
1413  }
1414  }
1415 
1416  mrmailbox_log_info(ths->m_mailbox, 0, "IMAP-restore-thread finished.");
1417 
1418 exit_:
1419  UNBLOCK_IDLE
1420  UNLOCK_HANDLE /* needed before the follow lock as the handle may be locked or unlocked when arriving in exit_*/
1421 
1422  if( fetch_result ) {
1423  mailimap_fetch_list_free(fetch_result);
1424  }
1425 
1426  if( folder_list ) {
1427  free_folders(folder_list);
1428  }
1429 
1430  LOCK_HANDLE
1431  ths->m_restore_thread_created = 0;
1432  UNLOCK_HANDLE
1433  mrosnative_unsetup_thread(ths->m_mailbox); /* must be very last */
1434  return NULL;
1435 }
1436 
1437 
1438 int mrimap_restore(mrimap_t* ths, time_t seconds_to_restore)
1439 {
1440  int success = 0, handle_locked = 0;
1441 
1442  if( ths==NULL || !ths->m_connected || seconds_to_restore <= 0 ) {
1443  goto cleanup;
1444  }
1445 
1446  LOCK_HANDLE
1447  if( ths->m_restore_thread_created ) {
1448  goto cleanup;
1449  }
1450  ths->m_restore_thread_created = 1;
1451  ths->m_restore_do_exit = 0;
1452  UNLOCK_HANDLE
1453 
1454  pthread_create(&ths->m_restore_thread, NULL, restore_thread_entry_point, ths);
1455 
1456  success = 1;
1457 
1458 cleanup:
1459  return success;
1460 }
1461 
1462 
1463 
1464  /*******************************************************************************
1465  * Main interface
1466  ******************************************************************************/
1467 
1468 
1469 mrimap_t* mrimap_new(mr_get_config_int_t get_config_int, mr_set_config_int_t set_config_int, mr_receive_imf_t receive_imf, void* userData, mrmailbox_t* mailbox)
1470 {
1471  mrimap_t* ths = NULL;
1472 
1473  if( (ths=calloc(1, sizeof(mrimap_t)))==NULL ) {
1474  exit(25); /* cannot allocate little memory, unrecoverable error */
1475  }
1476 
1477  ths->m_log_connect_errors = 1;
1478 
1479  ths->m_mailbox = mailbox;
1480  ths->m_get_config_int = get_config_int;
1481  ths->m_set_config_int = set_config_int;
1482  ths->m_receive_imf = receive_imf;
1483  ths->m_userData = userData;
1484 
1485  pthread_mutex_init(&ths->m_hEtpanmutex, NULL);
1486  pthread_mutex_init(&ths->m_idlemutex, NULL);
1487  pthread_mutex_init(&ths->m_inwait_mutex, NULL);
1488  pthread_mutex_init(&ths->m_watch_condmutex, NULL);
1489  pthread_cond_init(&ths->m_watch_cond, NULL);
1490 
1491  ths->m_enter_watch_wait_time = 0;
1492 
1493  pthread_mutex_init(&ths->m_heartbeat_condmutex, NULL);
1494  pthread_cond_init (&ths->m_heartbeat_cond, NULL);
1495 
1496  ths->m_selected_folder = calloc(1, 1);
1497  ths->m_moveto_folder = NULL;
1498  ths->m_sent_folder = NULL;
1499 
1500  /* create some useful objects */
1501  ths->m_fetch_type_uid = mailimap_fetch_type_new_fetch_att_list_empty(); /* object to fetch the ID */
1502  mailimap_fetch_type_new_fetch_att_list_add(ths->m_fetch_type_uid, mailimap_fetch_att_new_uid());
1503 
1504  ths->m_fetch_type_body = mailimap_fetch_type_new_fetch_att_list_empty(); /* object to fetch flags+body */
1505  mailimap_fetch_type_new_fetch_att_list_add(ths->m_fetch_type_body, mailimap_fetch_att_new_flags());
1506  mailimap_fetch_type_new_fetch_att_list_add(ths->m_fetch_type_body, mailimap_fetch_att_new_body_peek_section(mailimap_section_new(NULL)));
1507 
1508  ths->m_fetch_type_flags = mailimap_fetch_type_new_fetch_att_list_empty(); /* object to fetch flags only */
1509  mailimap_fetch_type_new_fetch_att_list_add(ths->m_fetch_type_flags, mailimap_fetch_att_new_flags());
1510 
1511  return ths;
1512 }
1513 
1514 
1515 void mrimap_unref(mrimap_t* ths)
1516 {
1517  if( ths==NULL ) {
1518  return;
1519  }
1520 
1521  mrimap_disconnect(ths);
1522 
1523  pthread_cond_destroy(&ths->m_heartbeat_cond);
1524  pthread_mutex_destroy(&ths->m_heartbeat_condmutex);
1525 
1526  pthread_cond_destroy(&ths->m_watch_cond);
1527  pthread_mutex_destroy(&ths->m_watch_condmutex);
1528  pthread_mutex_destroy(&ths->m_inwait_mutex);
1529  pthread_mutex_destroy(&ths->m_idlemutex);
1530  pthread_mutex_destroy(&ths->m_hEtpanmutex);
1531 
1532  free(ths->m_imap_server);
1533  free(ths->m_imap_user);
1534  free(ths->m_imap_pw);
1535  free(ths->m_selected_folder);
1536  free(ths->m_moveto_folder);
1537  free(ths->m_sent_folder);
1538 
1539  if( ths->m_fetch_type_uid ) { mailimap_fetch_type_free(ths->m_fetch_type_uid); }
1540  if( ths->m_fetch_type_body ) { mailimap_fetch_type_free(ths->m_fetch_type_body); }
1541  if( ths->m_fetch_type_flags ){ mailimap_fetch_type_free(ths->m_fetch_type_flags);}
1542 
1543  free(ths);
1544 }
1545 
1546 
1547 int mrimap_fetch(mrimap_t* ths)
1548 {
1549  if( ths==NULL || !ths->m_connected ) {
1550  return 0;
1551  }
1552 
1553  /* the following code has no effect in IDLE mode, however, it also does not disturb */
1554  pthread_mutex_lock(&ths->m_watch_condmutex);
1555  ths->m_watch_condflag = 1;
1556  pthread_cond_signal(&ths->m_watch_cond);
1557  pthread_mutex_unlock(&ths->m_watch_condmutex);
1558  return 1;
1559 }
1560 
1561 
1562 int mrimap_append_msg(mrimap_t* ths, time_t timestamp, const char* data_not_terminated, size_t data_bytes, char** ret_server_folder, uint32_t* ret_server_uid)
1563 {
1564  int success = 0, handle_locked = 0, idle_blocked = 0, r;
1565  uint32_t ret_uidvalidity = 0;
1566  struct mailimap_flag_list* flag_list = NULL;
1567  struct mailimap_date_time* imap_date = NULL;
1568 
1569  *ret_server_folder = NULL;
1570 
1571  if( ths==NULL ) {
1572  goto cleanup;
1573  }
1574 
1575  LOCK_HANDLE
1576 
1577  if( ths->m_hEtpan==NULL ) {
1578  goto cleanup;
1579  }
1580 
1581  BLOCK_IDLE
1582 
1583  INTERRUPT_IDLE
1584 
1585  mrmailbox_log_info(ths->m_mailbox, 0, "Appending message to IMAP-server...");
1586 
1587  if( !init_chat_folders__(ths) ) {
1588  mrmailbox_log_error(ths->m_mailbox, 0, "Cannot find out IMAP-sent-folder.");
1589  goto cleanup;
1590  }
1591 
1592  if( !select_folder__(ths, ths->m_sent_folder) ) {
1593  mrmailbox_log_error(ths->m_mailbox, 0, "Cannot select IMAP-folder \"%s\".", ths->m_sent_folder);
1594  ths->m_sent_folder[0] = 0; /* force re-init */
1595  goto cleanup;
1596  }
1597 
1598  flag_list = mailimap_flag_list_new_empty();
1599  mailimap_flag_list_add(flag_list, mailimap_flag_new_seen());
1600 
1601  imap_date = mr_timestamp_to_mailimap_date_time(timestamp);
1602  if( imap_date == NULL ) {
1603  mrmailbox_log_error(ths->m_mailbox, 0, "Bad date.");
1604  goto cleanup;
1605  }
1606 
1607  r = mailimap_uidplus_append(ths->m_hEtpan, ths->m_sent_folder, flag_list, imap_date, data_not_terminated, data_bytes, &ret_uidvalidity, ret_server_uid);
1608  if( is_error(ths, r) ) {
1609  mrmailbox_log_error(ths->m_mailbox, 0, "Cannot append message to \"%s\", error #%i.", ths->m_sent_folder, (int)r);
1610  goto cleanup;
1611  }
1612 
1613  *ret_server_folder = safe_strdup(ths->m_sent_folder);
1614 
1615  mrmailbox_log_info(ths->m_mailbox, 0, "Message appended to \"%s\".", ths->m_sent_folder);
1616 
1617  success = 1;
1618 
1619 cleanup:
1620  UNBLOCK_IDLE
1621  UNLOCK_HANDLE
1622 
1623  if( imap_date ) {
1624  mailimap_date_time_free(imap_date);
1625  }
1626 
1627  if( flag_list ) {
1628  mailimap_flag_list_free(flag_list);
1629  }
1630 
1631  return success;
1632 }
1633 
1634 
1635 static int add_flag__(mrimap_t* ths, const char* folder, uint32_t server_uid, struct mailimap_flag* flag)
1636 {
1637  int r;
1638  struct mailimap_flag_list* flag_list = NULL;
1639  struct mailimap_store_att_flags* store_att_flags = NULL;
1640  struct mailimap_set* set = mailimap_set_new_single(server_uid);
1641 
1642  if( ths==NULL || ths->m_hEtpan==NULL ) {
1643  goto cleanup;
1644  }
1645 
1646  if( select_folder__(ths, folder)==0 ) {
1647  goto cleanup;
1648  }
1649 
1650  flag_list = mailimap_flag_list_new_empty();
1651  mailimap_flag_list_add(flag_list, flag);
1652 
1653  store_att_flags = mailimap_store_att_flags_new_add_flags(flag_list); /* FLAGS.SILENT does not return the new value */
1654 
1655  r = mailimap_uid_store(ths->m_hEtpan, set, store_att_flags);
1656  if( is_error(ths, r) ) {
1657  goto cleanup;
1658  }
1659 
1660 cleanup:
1661  if( store_att_flags ) {
1662  mailimap_store_att_flags_free(store_att_flags);
1663  }
1664  if( set ) {
1665  mailimap_set_free(set);
1666  }
1667  return ths->m_should_reconnect? 0 : 1; /* all non-connection states are treated as success - the mail may already be deleted or moved away on the server */
1668 }
1669 
1670 
1671 int mrimap_markseen_msg(mrimap_t* ths, const char* folder, uint32_t server_uid, int ms_flags,
1672  char** ret_server_folder, uint32_t* ret_server_uid, int* ret_ms_flags)
1673 {
1674  // when marking as seen, there is no real need to check against the rfc724_mid - in the worst case, when the UID validity or the mailbox has changed, we mark the wrong message as "seen" - as the very most messages are seen, this is no big thing.
1675  // command would be "STORE 123,456,678 +FLAGS (\Seen)"
1676  int handle_locked = 0, idle_blocked = 0, r;
1677  struct mailimap_set* set = NULL;
1678 
1679  if( ths==NULL || folder==NULL || server_uid==0 || ret_server_folder==NULL || ret_server_uid==NULL || ret_ms_flags==NULL
1680  || *ret_server_folder!=NULL || *ret_server_uid!=0 || *ret_ms_flags!=0 ) {
1681  return 1; /* job done */
1682  }
1683 
1684  if( (set=mailimap_set_new_single(server_uid))==NULL ) {
1685  goto cleanup;
1686  }
1687 
1688  LOCK_HANDLE
1689 
1690  if( ths->m_hEtpan==NULL ) {
1691  goto cleanup;
1692  }
1693 
1694  BLOCK_IDLE
1695 
1696  INTERRUPT_IDLE
1697 
1698  mrmailbox_log_info(ths->m_mailbox, 0, "Marking message %s/%i as seen...", folder, (int)server_uid);
1699 
1700  if( add_flag__(ths, folder, server_uid, mailimap_flag_new_seen())==0 ) {
1701  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot mark message as seen.");
1702  goto cleanup;
1703  }
1704 
1705  mrmailbox_log_info(ths->m_mailbox, 0, "Message marked as seen.");
1706 
1707  if( (ms_flags&MR_MS_SET_MDNSent_FLAG)
1708  && ths->m_hEtpan->imap_selection_info!=NULL && ths->m_hEtpan->imap_selection_info->sel_perm_flags!=NULL )
1709  {
1710  /* Check if the folder can handle the `$MDNSent` flag (see RFC 3503). If so, and not set: set the flags and return this information.
1711  If the folder cannot handle the `$MDNSent` flag, we risk duplicated MDNs; it's up to the receiving MUA to handle this then (eg. Delta Chat has no problem with this). */
1712  int can_create_flag = 0;
1713  clistiter* iter;
1714  for( iter=clist_begin(ths->m_hEtpan->imap_selection_info->sel_perm_flags); iter!=NULL; iter=clist_next(iter) )
1715  {
1716  struct mailimap_flag_perm* fp = (struct mailimap_flag_perm*)clist_content(iter);
1717  if( fp ) {
1718  if( fp->fl_type==MAILIMAP_FLAG_PERM_ALL ) {
1719  can_create_flag = 1;
1720  break;
1721  }
1722  else if( fp->fl_type==MAILIMAP_FLAG_PERM_FLAG && fp->fl_flag ) {
1723  struct mailimap_flag* fl = (struct mailimap_flag*)fp->fl_flag;
1724  if( fl->fl_type==MAILIMAP_FLAG_KEYWORD && fl->fl_data.fl_keyword && strcmp(fl->fl_data.fl_keyword, "$MDNSent")==0 ) {
1725  can_create_flag = 1;
1726  break;
1727  }
1728  }
1729  }
1730  }
1731 
1732  if( can_create_flag )
1733  {
1734  clist* fetch_result = NULL;
1735  r = mailimap_uid_fetch(ths->m_hEtpan, set, ths->m_fetch_type_flags, &fetch_result);
1736  if( !is_error(ths, r) && fetch_result ) {
1737  clistiter* cur=clist_begin(fetch_result);
1738  if( cur ) {
1739  if( !peek_flag_keyword((struct mailimap_msg_att*)clist_content(cur), "$MDNSent") ) {
1740  add_flag__(ths, folder, server_uid, mailimap_flag_new_flag_keyword(safe_strdup("$MDNSent")));
1741  *ret_ms_flags |= MR_MS_MDNSent_JUST_SET;
1742  }
1743  }
1744  mailimap_fetch_list_free(fetch_result);
1745  }
1746  mrmailbox_log_info(ths->m_mailbox, 0, ((*ret_ms_flags)&MR_MS_MDNSent_JUST_SET)? "$MDNSent just set and MDN will be send." : "$MDNSent already set and MDN already send.");
1747  }
1748  else
1749  {
1750  *ret_ms_flags |= MR_MS_MDNSent_JUST_SET;
1751  mrmailbox_log_info(ths->m_mailbox, 0, "Cannot store $MDNSent flags, risk sending duplicate MDN.");
1752  }
1753  }
1754 
1755  if( (ms_flags&MR_MS_ALSO_MOVE) && (ths->m_server_flags&MR_NO_MOVE_TO_CHATS)==0 )
1756  {
1757  init_chat_folders__(ths);
1758  if( ths->m_moveto_folder && strcmp(folder, ths->m_moveto_folder)==0 )
1759  {
1760  mrmailbox_log_info(ths->m_mailbox, 0, "Message %s/%i is already in %s...", folder, (int)server_uid, ths->m_moveto_folder);
1761  /* avoid deadlocks as moving messages in the same folder may be result in a new server_uid and the state "fresh" -
1762  we will catch these messages again on the next pull, try to move them away and so on, see also (***) */
1763  }
1764  else if( ths->m_moveto_folder )
1765  {
1766  mrmailbox_log_info(ths->m_mailbox, 0, "Moving message %s/%i to %s...", folder, (int)server_uid, ths->m_moveto_folder);
1767 
1768  /* TODO/TOCHECK: MOVE may not be supported on servers, if this is often the case, we should fallback to a COPY/DELETE implementation.
1769  Same for the UIDPLUS extension (if in doubt, we can find out the resulting UID using "imap_selection_info->sel_uidnext" then). */
1770  uint32_t res_uid = 0;
1771  struct mailimap_set* res_setsrc = NULL;
1772  struct mailimap_set* res_setdest = NULL;
1773  r = mailimap_uidplus_uid_move(ths->m_hEtpan, set, ths->m_moveto_folder, &res_uid, &res_setsrc, &res_setdest); /* the correct folder is already selected in add_flag__() above */
1774  if( is_error(ths, r) ) {
1775  mrmailbox_log_info(ths->m_mailbox, 0, "Cannot move message.");
1776  goto cleanup;
1777  }
1778 
1779  if( res_setsrc ) {
1780  mailimap_set_free(res_setsrc);
1781  }
1782 
1783  if( res_setdest ) {
1784  clistiter* cur = clist_begin(res_setdest->set_list);
1785  if (cur != NULL) {
1786  struct mailimap_set_item* item;
1787  item = clist_content(cur);
1788  *ret_server_uid = item->set_first;
1789  *ret_server_folder = safe_strdup(ths->m_moveto_folder);
1790  }
1791  mailimap_set_free(res_setdest);
1792  }
1793 
1794  // TODO: If the new UID is equal to lastuid.Chats, we should increase lastuid.Chats by one
1795  // (otherwise, we'll download the mail in moment again from the chats folder ...)
1796 
1797  mrmailbox_log_info(ths->m_mailbox, 0, "Message moved.");
1798  }
1799  }
1800 
1801 cleanup:
1802  UNBLOCK_IDLE
1803  UNLOCK_HANDLE
1804  if( set ) {
1805  mailimap_set_free(set);
1806  }
1807  return ths->m_should_reconnect? 0 : 1;
1808 }
1809 
1810 
1811 int mrimap_delete_msg(mrimap_t* ths, const char* rfc724_mid, const char* folder, uint32_t server_uid)
1812 {
1813  // when deleting using server_uid, we have to check against rfc724_mid first - the UID validity or the mailbox may have change
1814  int success = 0, handle_locked = 0, idle_blocked = 0;
1815 
1816  if( ths==NULL || rfc724_mid==NULL || folder==NULL || folder[0]==0 || server_uid==0 ) {
1817  return 1; /* job done */
1818  }
1819 
1820  LOCK_HANDLE
1821  BLOCK_IDLE
1822 
1823  INTERRUPT_IDLE
1824 
1825  mrmailbox_log_info(ths->m_mailbox, 0, "Deleting message \"%s\", server_folder=%s, server_uid=%i...", rfc724_mid, folder, (int)server_uid);
1826 
1827  if( add_flag__(ths, folder, server_uid, mailimap_flag_new_deleted())==0 ) {
1828  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot delete message."); /* maybe the message is already deleted */
1829  goto cleanup;
1830  }
1831 
1832  mrmailbox_log_info(ths->m_mailbox, 0, "Message deleted.");
1833 
1834  success = 1;
1835 
1836 cleanup:
1837  UNBLOCK_IDLE
1838  UNLOCK_HANDLE
1839 
1840  return success;
1841 }
1842 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrjob_8c_source.html b/docs/user/html/mrjob_8c_source.html deleted file mode 100644 index 890695f9..00000000 --- a/docs/user/html/mrjob_8c_source.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrjob.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrjob.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrjob.h"
25 #include "mrosnative.h"
26 
27 
28 /*******************************************************************************
29  * The job thread
30  ******************************************************************************/
31 
32 
33 static int get_wait_seconds(mrmailbox_t* mailbox) // >0: wait seconds, =0: do not wait, <0: wait until signal
34 {
35  int ret = -1;
36  sqlite3_stmt* stmt;
37 
38  mrsqlite3_lock(mailbox->m_sql);
39  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_MIN_d_FROM_jobs, "SELECT MIN(desired_timestamp) FROM jobs;");
40  if( stmt && sqlite3_step(stmt) == SQLITE_ROW )
41  {
42  if( sqlite3_column_type(stmt, 0)!=SQLITE_NULL )
43  {
44  time_t min_desired_timestamp = (time_t)sqlite3_column_int64(stmt, 0);
45  time_t now = time(NULL);
46  if( min_desired_timestamp <= now ) {
47  ret = 0;
48  }
49  else {
50  ret = (int)(min_desired_timestamp-now) + 1 /*wait a second longer, pthread_cond_timedwait() is not _that_ exact and we want to be sure to catch the jobs in the first try*/;
51  }
52  }
53  }
54  mrsqlite3_unlock(mailbox->m_sql);
55 
56  return ret;
57 }
58 
59 
60 static void* job_thread_entry_point(void* entry_arg)
61 {
62  mrmailbox_t* mailbox = (mrmailbox_t*)entry_arg;
63  mrosnative_setup_thread(mailbox); /* must be very first */
64 
65  sqlite3_stmt* stmt;
66  mrjob_t job;
67  int seconds_to_wait;
68 
69  memset(&job, 0, sizeof(mrjob_t));
70  job.m_param = mrparam_new();
71 
72  /* init thread */
73  mrmailbox_log_info(mailbox, 0, "Job thread entered.");
74 
75  while( 1 )
76  {
77  /* wait for condition */
78  pthread_mutex_lock(&mailbox->m_job_condmutex);
79  seconds_to_wait = get_wait_seconds(mailbox);
80  if( seconds_to_wait > 0 ) {
81  mrmailbox_log_info(mailbox, 0, "Job thread waiting for %i seconds or signal...", seconds_to_wait);
82  if( mailbox->m_job_condflag == 0 ) {
83  struct timespec timeToWait;
84  timeToWait.tv_sec = time(NULL)+seconds_to_wait;
85  timeToWait.tv_nsec = 0;
86  pthread_cond_timedwait(&mailbox->m_job_cond, &mailbox->m_job_condmutex, &timeToWait);
87  }
88  }
89  else if( seconds_to_wait < 0 ) {
90  mrmailbox_log_info(mailbox, 0, "Job thread waiting for signal...");
91  while( mailbox->m_job_condflag == 0 ) {
92  pthread_cond_wait(&mailbox->m_job_cond, &mailbox->m_job_condmutex); /* wait unlocks the mutex and waits for signal; if it returns, the mutex is locked again */
93  }
94  }
95  mailbox->m_job_condflag = 0;
96  pthread_mutex_unlock(&mailbox->m_job_condmutex);
97 
98  /* do all waiting jobs */
99  mrmailbox_log_info(mailbox, 0, "Job thread checks for pending jobs...");
100  while( 1 )
101  {
102  pthread_mutex_lock(&mailbox->m_job_condmutex);
103  if( mailbox->m_job_do_exit ) {
104  pthread_mutex_unlock(&mailbox->m_job_condmutex);
105  goto exit_;
106  }
107  pthread_mutex_unlock(&mailbox->m_job_condmutex);
108 
109  /* get next waiting job */
110  job.m_job_id = 0;
111  mrsqlite3_lock(mailbox->m_sql);
112  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_iafp_FROM_jobs,
113  "SELECT id, action, foreign_id, param FROM jobs WHERE desired_timestamp<=? ORDER BY action DESC, id LIMIT 1;");
114  sqlite3_bind_int64(stmt, 1, time(NULL));
115  if( sqlite3_step(stmt) == SQLITE_ROW ) {
116  job.m_job_id = sqlite3_column_int (stmt, 0);
117  job.m_action = sqlite3_column_int (stmt, 1);
118  job.m_foreign_id = sqlite3_column_int (stmt, 2);
119  mrparam_set_packed(job.m_param, (char*)sqlite3_column_text(stmt, 3));
120  }
121  mrsqlite3_unlock(mailbox->m_sql);
122 
123  if( job.m_job_id == 0 ) {
124  break;
125  }
126 
127  /* execute job */
128  mrmailbox_log_info(mailbox, 0, "Executing job #%i, action %i...", (int)job.m_job_id, (int)job.m_action);
129  job.m_start_again_at = 0;
130  switch( job.m_action ) {
131  case MRJ_CONNECT_TO_IMAP: mrmailbox_connect_to_imap (mailbox, &job); break;
132  case MRJ_SEND_MSG_TO_SMTP: mrmailbox_send_msg_to_smtp (mailbox, &job); break;
133  case MRJ_SEND_MSG_TO_IMAP: mrmailbox_send_msg_to_imap (mailbox, &job); break;
134  case MRJ_DELETE_MSG_ON_IMAP: mrmailbox_delete_msg_on_imap (mailbox, &job); break;
135  case MRJ_MARKSEEN_MSG_ON_IMAP: mrmailbox_markseen_msg_on_imap (mailbox, &job); break;
136  case MRJ_MARKSEEN_MDN_ON_IMAP: mrmailbox_markseen_mdn_on_imap (mailbox, &job); break;
137  case MRJ_SEND_MDN: mrmailbox_send_mdn (mailbox, &job); break;
138  }
139 
140  /* delete job or execute job later again */
141  if( job.m_start_again_at ) {
142  mrsqlite3_lock(mailbox->m_sql);
143  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_jobs_SET_dp_WHERE_id,
144  "UPDATE jobs SET desired_timestamp=?, param=? WHERE id=?;");
145  sqlite3_bind_int64(stmt, 1, job.m_start_again_at);
146  sqlite3_bind_text (stmt, 2, job.m_param->m_packed, -1, SQLITE_STATIC);
147  sqlite3_bind_int (stmt, 3, job.m_job_id);
148  sqlite3_step(stmt);
149  mrsqlite3_unlock(mailbox->m_sql);
150  mrmailbox_log_info(mailbox, 0, "Job #%i delayed for %i seconds", (int)job.m_job_id, (int)(job.m_start_again_at-time(NULL)));
151  }
152  else {
153  mrsqlite3_lock(mailbox->m_sql);
154  stmt = mrsqlite3_predefine__(mailbox->m_sql, DELETE_FROM_jobs_WHERE_id,
155  "DELETE FROM jobs WHERE id=?;");
156  sqlite3_bind_int(stmt, 1, job.m_job_id);
157  sqlite3_step(stmt);
158  mrsqlite3_unlock(mailbox->m_sql);
159  mrmailbox_log_info(mailbox, 0, "Job #%i done and deleted from database", (int)job.m_job_id);
160  }
161  }
162 
163  }
164 
165  /* exit thread */
166 exit_:
167  mrparam_unref(job.m_param);
168  mrmailbox_log_info(mailbox, 0, "Exit job thread.");
169  mrosnative_unsetup_thread(mailbox); /* must be very last */
170  return NULL;
171 }
172 
173 
174 /*******************************************************************************
175  * Main interface
176  ******************************************************************************/
177 
178 
179 void mrjob_init_thread(mrmailbox_t* mailbox)
180 {
181  pthread_mutex_init(&mailbox->m_job_condmutex, NULL);
182  pthread_cond_init(&mailbox->m_job_cond, NULL);
183  pthread_create(&mailbox->m_job_thread, NULL, job_thread_entry_point, mailbox);
184 }
185 
186 
187 void mrjob_exit_thread(mrmailbox_t* mailbox)
188 {
189  pthread_mutex_lock(&mailbox->m_job_condmutex);
190  mailbox->m_job_condflag = 1;
191  mailbox->m_job_do_exit = 1;
192  pthread_cond_signal(&mailbox->m_job_cond);
193  pthread_mutex_unlock(&mailbox->m_job_condmutex);
194 
195  pthread_join(mailbox->m_job_thread, NULL);
196  pthread_cond_destroy(&mailbox->m_job_cond);
197  pthread_mutex_destroy(&mailbox->m_job_condmutex);
198 }
199 
200 
201 uint32_t mrjob_add__(mrmailbox_t* mailbox, int action, int foreign_id, const char* param)
202 {
203  time_t timestamp = time(NULL);
204  sqlite3_stmt* stmt;
205  uint32_t job_id = 0;
206 
207  stmt = mrsqlite3_predefine__(mailbox->m_sql, INSERT_INTO_jobs_aafp,
208  "INSERT INTO jobs (added_timestamp, action, foreign_id, param) VALUES (?,?,?,?);");
209  sqlite3_bind_int64(stmt, 1, timestamp);
210  sqlite3_bind_int (stmt, 2, action);
211  sqlite3_bind_int (stmt, 3, foreign_id);
212  sqlite3_bind_text (stmt, 4, param? param : "", -1, SQLITE_STATIC);
213  if( sqlite3_step(stmt) != SQLITE_DONE ) {
214  return 0;
215  }
216 
217  job_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
218 
219  pthread_mutex_lock(&mailbox->m_job_condmutex);
220  if( !mailbox->m_job_do_exit ) {
221  mrmailbox_log_info(mailbox, 0, "Signal job thread to wake up...");
222  mailbox->m_job_condflag = 1;
223  pthread_cond_signal(&mailbox->m_job_cond);
224  }
225  pthread_mutex_unlock(&mailbox->m_job_condmutex);
226 
227  return job_id;
228 }
229 
230 
231 void mrjob_try_again_later(mrjob_t* ths, int initial_delay_seconds)
232 {
233  if( ths == NULL ) { /* may be NULL if called eg. from mrmailbox_connect_to_imap() */
234  return;
235  }
236 
237  if( initial_delay_seconds == MR_INCREATION_POLL )
238  {
239  int tries = mrparam_get_int(ths->m_param, MRP_TIMES_INCREATION, 0) + 1;
240  mrparam_set_int(ths->m_param, MRP_TIMES_INCREATION, tries);
241 
242  if( tries < 120/MR_INCREATION_POLL ) {
243  ths->m_start_again_at = time(NULL)+MR_INCREATION_POLL;
244  }
245  else {
246  ths->m_start_again_at = time(NULL)+10; /* after two minutes of waiting, try less often */
247  }
248  }
249  else
250  {
251  int tries = mrparam_get_int(ths->m_param, MRP_TIMES, 0) + 1;
252  mrparam_set_int(ths->m_param, MRP_TIMES, tries);
253 
254  if( tries == 1 ) {
255  ths->m_start_again_at = time(NULL)+initial_delay_seconds;
256  }
257  else if( tries < 5 ) {
258  ths->m_start_again_at = time(NULL)+60;
259  }
260  else {
261  ths->m_start_again_at = time(NULL)+600;
262  }
263  }
264 }
265 
266 
267 void mrjob_kill_action__(mrmailbox_t* mailbox, int action)
268 {
269  if( mailbox == NULL ) {
270  return;
271  }
272 
273  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, DELETE_FROM_jobs_WHERE_action,
274  "DELETE FROM jobs WHERE action=?;");
275  sqlite3_bind_int(stmt, 1, action);
276  sqlite3_step(stmt);
277 }
278 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
-
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
-
void mrparam_set_int(mrparam_t *param, int key, int32_t value)
Set parameter to an integer.
Definition: mrparam.c:318
-
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
-
- - - - diff --git a/docs/user/html/mrkey_8c_source.html b/docs/user/html/mrkey_8c_source.html deleted file mode 100644 index 804c49f2..00000000 --- a/docs/user/html/mrkey_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrkey.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrkey.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include <memory.h>
25 #include "mrkey.h"
26 #include "mrpgp.h"
27 #include "mrtools.h"
28 
29 
30 /*******************************************************************************
31  * Main interface
32  ******************************************************************************/
33 
34 
35 void mr_wipe_secret_mem(void* buf, size_t buf_bytes)
36 {
37  /* wipe private keys or othere secrets with zeros so that secrets are no longer in RAM */
38  if( buf == NULL || buf_bytes <= 0 ) {
39  return;
40  }
41 
42  memset(buf, 0x00, buf_bytes);
43 }
44 
45 
46 static void mrkey_empty(mrkey_t* ths) /* only use before calling setters; take care when using this function together with reference counting, prefer new objects instead */
47 {
48  if( ths == NULL ) {
49  return;
50  }
51 
52  if( ths->m_type==MR_PRIVATE ) {
53  mr_wipe_secret_mem(ths->m_binary, ths->m_bytes);
54  }
55 
56  free(ths->m_binary);
57  ths->m_binary = NULL;
58  ths->m_bytes = 0;
59  ths->m_type = MR_PUBLIC;
60 }
61 
62 
63 mrkey_t* mrkey_new()
64 {
65  mrkey_t* ths;
66 
67  if( (ths=calloc(1, sizeof(mrkey_t)))==NULL ) {
68  exit(44); /* cannot allocate little memory, unrecoverable error */
69  }
70  ths->_m_heap_refcnt = 1;
71  return ths;
72 }
73 
74 
75 mrkey_t* mrkey_ref(mrkey_t* ths)
76 {
77  if( ths==NULL ) {
78  return NULL;
79  }
80  ths->_m_heap_refcnt++;
81  return ths;
82 }
83 
84 
85 void mrkey_unref(mrkey_t* ths)
86 {
87  if( ths==NULL ) {
88  return;
89  }
90 
91  ths->_m_heap_refcnt--;
92  if( ths->_m_heap_refcnt != 0 ) {
93  return;
94  }
95 
96  mrkey_empty(ths);
97  free(ths);
98 }
99 
100 
101 int mrkey_set_from_raw(mrkey_t* ths, const void* data, int bytes, int type)
102 {
103  mrkey_empty(ths);
104  if( ths==NULL || data==NULL || bytes <= 0 ) {
105  return 0;
106  }
107  ths->m_binary = malloc(bytes);
108  if( ths->m_binary == NULL ) {
109  exit(40);
110  }
111  memcpy(ths->m_binary, data, bytes);
112  ths->m_bytes = bytes;
113  ths->m_type = type;
114  return 1;
115 }
116 
117 
118 int mrkey_set_from_key(mrkey_t* ths, const mrkey_t* o)
119 {
120  mrkey_empty(ths);
121  if( ths==NULL || o==NULL ) {
122  return 0;
123  }
124  return mrkey_set_from_raw(ths, o->m_binary, o->m_bytes, o->m_type);
125 }
126 
127 
128 int mrkey_set_from_stmt(mrkey_t* ths, sqlite3_stmt* stmt, int index, int type)
129 {
130  mrkey_empty(ths);
131  if( ths==NULL || stmt==NULL ) {
132  return 0;
133  }
134  return mrkey_set_from_raw(ths, (unsigned char*)sqlite3_column_blob(stmt, index), sqlite3_column_bytes(stmt, index), type);
135 }
136 
137 
138 int mrkey_set_from_base64(mrkey_t* ths, const char* base64, int type)
139 {
140  size_t indx = 0, result_len = 0;
141  char* result = NULL;
142 
143  mrkey_empty(ths);
144 
145  if( ths==NULL || base64==NULL ) {
146  return 0;
147  }
148 
149  if( mailmime_base64_body_parse(base64, strlen(base64), &indx, &result/*must be freed using mmap_string_unref()*/, &result_len)!=MAILIMF_NO_ERROR
150  || result == NULL || result_len == 0 ) {
151  return 0; /* bad key */
152  }
153 
154  mrkey_set_from_raw(ths, result, result_len, type);
155  mmap_string_unref(result);
156 
157  return 1;
158 }
159 
160 
161 int mrkey_set_from_file(mrkey_t* ths, const char* pathNfilename, mrmailbox_t* mailbox)
162 {
163  char* buf = NULL;
164  char *p1, *p2; /* just pointers inside buf, must not be freed */
165  size_t buf_bytes;
166  int type = -1, success = 0;
167 
168  mrkey_empty(ths);
169 
170  if( ths==NULL || pathNfilename==NULL ) {
171  goto cleanup;
172  }
173 
174  if( !mr_read_file(pathNfilename, (void**)&buf, &buf_bytes, mailbox)
175  || buf_bytes < 50 ) {
176  goto cleanup; /* error is already loged */
177  }
178 
179  mr_remove_cr_chars(buf); /* make comparison easier */
180  mr_trim(buf);
181 
182  if( strncmp(buf, "-----BEGIN PGP PUBLIC KEY BLOCK-----\n", 37)==0 ) {
183  if( mr_str_replace(&buf, "-----END PGP PUBLIC KEY BLOCK-----", "")!=1 ) {
184  mrmailbox_log_warning(mailbox, 0, "Bad header for key \"%s\".", pathNfilename);
185  goto cleanup;
186  }
187  type = MR_PUBLIC;
188  p1 = buf + 37; /* must be done after buf-pointer modification in mr_str_replace() */
189  }
190  else if( strncmp(buf, "-----BEGIN PGP PRIVATE KEY BLOCK-----\n", 38)==0 ) {
191  if( mr_str_replace(&buf, "-----END PGP PRIVATE KEY BLOCK-----", "")!=1 ) {
192  mrmailbox_log_warning(mailbox, 0, "Bad header for key \"%s\".", pathNfilename);
193  goto cleanup;
194  }
195  type = MR_PRIVATE;
196  p1 = buf + 38; /* must be done after buf-pointer modification in mr_str_replace() */
197  }
198  else {
199  mrmailbox_log_warning(mailbox, 0, "Header missing for key \"%s\".", pathNfilename);
200  goto cleanup;
201  }
202 
203  /* base64 starts after first empty line, if any */
204  p2 = strstr(p1, "\n\n"); /* `\r* is already removed above */
205  if( p2 ) {
206  p1 = p2;
207  }
208 
209  if( !mrkey_set_from_base64(ths, p1, type) ) {
210  mrmailbox_log_warning(mailbox, 0, "Bad data in key \"%s\".", pathNfilename);
211  goto cleanup;
212  }
213 
214  success = 1;
215 
216 cleanup:
217  free(buf);
218  return success;
219 }
220 
221 
222 int mrkey_equals(const mrkey_t* ths, const mrkey_t* o)
223 {
224  if( ths==NULL || o==NULL
225  || ths->m_binary==NULL || ths->m_bytes<=0 || o->m_binary==NULL || o->m_bytes<=0 ) {
226  return 0; /*error*/
227  }
228 
229  if( ths->m_bytes != o->m_bytes ) {
230  return 0; /*different size -> the keys cannot be equal*/
231  }
232 
233  if( ths->m_type != o->m_type ) {
234  return 0; /* cannot compare public with private keys */
235  }
236 
237  return memcmp(ths->m_binary, o->m_binary, o->m_bytes)==0? 1 : 0;
238 }
239 
240 
241 /*******************************************************************************
242  * Save/Load keys
243  ******************************************************************************/
244 
245 
246 int mrkey_save_self_keypair__(const mrkey_t* public_key, const mrkey_t* private_key, const char* addr, int is_default, mrsqlite3_t* sql)
247 {
248  sqlite3_stmt* stmt;
249 
250  if( public_key==NULL || private_key==NULL || addr==NULL || sql==NULL
251  || public_key->m_binary==NULL || private_key->m_binary==NULL ) {
252  return 0;
253  }
254 
255  stmt = mrsqlite3_predefine__(sql, INSERT_INTO_keypairs_aippc,
256  "INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);");
257  sqlite3_bind_text (stmt, 1, addr, -1, SQLITE_STATIC);
258  sqlite3_bind_int (stmt, 2, is_default);
259  sqlite3_bind_blob (stmt, 3, public_key->m_binary, public_key->m_bytes, SQLITE_STATIC);
260  sqlite3_bind_blob (stmt, 4, private_key->m_binary, private_key->m_bytes, SQLITE_STATIC);
261  sqlite3_bind_int64(stmt, 5, time(NULL));
262  if( sqlite3_step(stmt) != SQLITE_DONE ) {
263  return 0;
264  }
265 
266  return 1;
267 }
268 
269 
270 int mrkey_load_self_public__(mrkey_t* ths, const char* self_addr, mrsqlite3_t* sql)
271 {
272  sqlite3_stmt* stmt;
273 
274  if( ths==NULL || self_addr==NULL || sql==NULL ) {
275  return 0;
276  }
277 
278  mrkey_empty(ths);
279  stmt = mrsqlite3_predefine__(sql, SELECT_public_key_FROM_keypairs_WHERE_default,
280  "SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;");
281  sqlite3_bind_text (stmt, 1, self_addr, -1, SQLITE_STATIC);
282  if( sqlite3_step(stmt) != SQLITE_ROW ) {
283  return 0;
284  }
285  mrkey_set_from_stmt(ths, stmt, 0, MR_PUBLIC);
286  return 1;
287 }
288 
289 
290 int mrkey_load_self_private__(mrkey_t* ths, const char* self_addr, mrsqlite3_t* sql)
291 {
292  sqlite3_stmt* stmt;
293 
294  if( ths==NULL || self_addr==NULL || sql==NULL ) {
295  return 0;
296  }
297 
298  mrkey_empty(ths);
299  stmt = mrsqlite3_predefine__(sql, SELECT_private_key_FROM_keypairs_WHERE_default,
300  "SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;");
301  sqlite3_bind_text (stmt, 1, self_addr, -1, SQLITE_STATIC);
302  if( sqlite3_step(stmt) != SQLITE_ROW ) {
303  return 0;
304  }
305  mrkey_set_from_stmt(ths, stmt, 0, MR_PRIVATE);
306  return 1;
307 }
308 
309 
310 /*******************************************************************************
311  * Render keys
312  ******************************************************************************/
313 
314 
315 static long crc_octets(const unsigned char *octets, size_t len)
316 {
317  #define CRC24_INIT 0xB704CEL
318  #define CRC24_POLY 0x1864CFBL
319  long crc = CRC24_INIT;
320  int i;
321  while (len--) {
322  crc ^= (*octets++) << 16;
323  for (i = 0; i < 8; i++) {
324  crc <<= 1;
325  if (crc & 0x1000000)
326  crc ^= CRC24_POLY;
327  }
328  }
329  return crc & 0xFFFFFFL;
330 }
331 
332 
333 char* mr_render_base64(const void* buf, size_t buf_bytes, int break_every, const char* break_chars,
334  int add_checksum /*0=no checksum, 1=add without break, 2=add with break_chars*/)
335 {
336  char* ret = NULL;
337 
338  if( buf==NULL || buf_bytes<=0 ) {
339  goto cleanup;
340  }
341 
342  if( (ret = encode_base64((const char*)buf, buf_bytes))==NULL ) {
343  goto cleanup;
344  }
345 
346  #if 0
347  if( add_checksum == 1/*appended checksum*/ ) {
348  long checksum = crc_octets(buf, buf_bytes);
349  uint8_t c[3];
350  c[0] = (uint8_t)((checksum >> 16)&0xFF);
351  c[1] = (uint8_t)((checksum >> 8)&0xFF);
352  c[2] = (uint8_t)((checksum)&0xFF);
353  char* c64 = encode_base64((const char*)c, 3);
354  char* temp = ret;
355  ret = mr_mprintf("%s=%s", temp, c64);
356  free(temp);
357  free(c64);
358  }
359  #endif
360 
361  if( break_every>0 ) {
362  char* temp = ret;
363  ret = mr_insert_breaks(temp, break_every, break_chars);
364  free(temp);
365  }
366 
367  if( add_checksum == 2/*checksum with break character*/ ) {
368  long checksum = crc_octets(buf, buf_bytes);
369  uint8_t c[3];
370  c[0] = (uint8_t)((checksum >> 16)&0xFF);
371  c[1] = (uint8_t)((checksum >> 8)&0xFF);
372  c[2] = (uint8_t)((checksum)&0xFF);
373  char* c64 = encode_base64((const char*)c, 3);
374  char* temp = ret;
375  ret = mr_mprintf("%s%s=%s", temp, break_chars, c64);
376  free(temp);
377  free(c64);
378  }
379 
380 cleanup:
381  return ret;
382 }
383 
384 
385 char* mrkey_render_base64(const mrkey_t* ths, int break_every, const char* break_chars, int add_checksum)
386 {
387  if( ths==NULL ) {
388  return NULL;
389  }
390  return mr_render_base64(ths->m_binary, ths->m_bytes, break_every, break_chars, add_checksum);
391 }
392 
393 
394 char* mrkey_render_asc(const mrkey_t* ths, const char* add_header_lines /*must be terminated by \r\n*/)
395 {
396  /* see RFC 4880, 6.2. Forming ASCII Armor, https://tools.ietf.org/html/rfc4880#section-6.2 */
397  char *base64 = NULL, *ret = NULL;
398 
399  if( ths==NULL ) {
400  goto cleanup;
401  }
402 
403  if( (base64=mrkey_render_base64(ths, 76, "\r\n", 2/*checksum in new line*/))==NULL ) { /* RFC: The encoded output stream must be represented in lines of no more than 76 characters each. */
404  goto cleanup;
405  }
406 
407  ret = mr_mprintf("-----BEGIN PGP %s KEY BLOCK-----\r\n%s\r\n%s\r\n-----END PGP %s KEY BLOCK-----\r\n",
408  ths->m_type==MR_PUBLIC? "PUBLIC" : "PRIVATE",
409  add_header_lines? add_header_lines : "",
410  base64,
411  ths->m_type==MR_PUBLIC? "PUBLIC" : "PRIVATE");
412 
413 cleanup:
414  free(base64);
415  return ret;
416 }
417 
418 
419 char* mr_render_fingerprint(const uint8_t* data, size_t bytes)
420 {
421  int i;
422  char* temp;
423 
424  if( data ==NULL || bytes <= 0 ) {
425  return safe_strdup("ErrFingerprint2");
426  }
427 
428  char* ret = malloc(bytes*4+1); if( ret==NULL ) { exit(46); }
429  ret[0] = 0;
430 
431  for( i = 0; i < bytes; i++ ) {
432  temp = mr_mprintf("%02X%s", (int)data[i], (i==6||i==13)?"\n":" ");
433  strcat(ret, temp);
434  free(temp);
435  }
436 
437  return ret;
438 }
439 
440 
441 char* mrkey_render_fingerprint(const mrkey_t* key, mrmailbox_t* mailbox)
442 {
443  uint8_t* fingerprint_buf = NULL;
444  size_t fingerprint_bytes = 0;
445 
446  if( key==NULL || mailbox == NULL ) {
447  return safe_strdup("ErrFingerprint0");
448  }
449 
450  if( !mrpgp_calc_fingerprint(mailbox, key, &fingerprint_buf, &fingerprint_bytes) ) {
451  return safe_strdup("ErrFingerprint1");
452  }
453 
454  char* fingerprint_str = mr_render_fingerprint(fingerprint_buf, fingerprint_bytes);
455  free(fingerprint_buf);
456  return fingerprint_str;
457 }
458 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrkeyring_8c_source.html b/docs/user/html/mrkeyring_8c_source.html deleted file mode 100644 index f92572cd..00000000 --- a/docs/user/html/mrkeyring_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrkeyring.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrkeyring.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include <memory.h>
25 #include "mrkey.h"
26 #include "mrkeyring.h"
27 #include "mrtools.h"
28 
29 
30 /*******************************************************************************
31  * Main interface
32  ******************************************************************************/
33 
34 
35 mrkeyring_t* mrkeyring_new()
36 {
37  mrkeyring_t* ths;
38 
39  if( (ths=calloc(1, sizeof(mrkeyring_t)))==NULL ) {
40  exit(42); /* cannot allocate little memory, unrecoverable error */
41  }
42  return ths;
43 }
44 
45 
46 void mrkeyring_unref(mrkeyring_t* ths)
47 {
48  int i;
49  if( ths == NULL ) {
50  return;
51  }
52 
53  for( i = 0; i < ths->m_count; i++ ) {
54  mrkey_unref(ths->m_keys[i]);
55  }
56  free(ths->m_keys);
57  free(ths);
58 }
59 
60 
61 void mrkeyring_add(mrkeyring_t* ths, mrkey_t* to_add)
62 {
63  if( ths==NULL || to_add==NULL ) {
64  return;
65  }
66 
67  /* expand array, if needed */
68  if( ths->m_count == ths->m_allocated ) {
69  int newsize = (ths->m_allocated * 2) + 10;
70  if( (ths->m_keys=realloc(ths->m_keys, newsize*sizeof(mrkey_t*)))==NULL ) {
71  exit(41);
72  }
73  ths->m_allocated = newsize;
74  }
75 
76  ths->m_keys[ths->m_count] = mrkey_ref(to_add);
77  ths->m_count++;
78 }
79 
80 
81 int mrkeyring_load_self_private_for_decrypting__(mrkeyring_t* ths, const char* self_addr, mrsqlite3_t* sql)
82 {
83  sqlite3_stmt* stmt;
84  mrkey_t* key;
85 
86  if( ths==NULL || self_addr==NULL || sql==NULL ) {
87  return 0;
88  }
89 
90  stmt = mrsqlite3_predefine__(sql, SELECT_private_key_FROM_keypairs_ORDER_BY_default,
91  "SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;");
92  sqlite3_bind_text (stmt, 1, self_addr, -1, SQLITE_STATIC);
93  while( sqlite3_step(stmt) == SQLITE_ROW ) {
94  key = mrkey_new();
95  if( mrkey_set_from_stmt(key, stmt, 0, MR_PRIVATE) ) {
96  mrkeyring_add(ths, key);
97  }
98  mrkey_unref(key); /* unref in any case, mrkeyring_add() adds its own reference */
99  }
100 
101  return 1;
102 }
103 
- - - - diff --git a/docs/user/html/mrloginparam_8c_source.html b/docs/user/html/mrloginparam_8c_source.html deleted file mode 100644 index 4e524f7f..00000000 --- a/docs/user/html/mrloginparam_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrloginparam.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrloginparam.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrloginparam.h"
25 
26 
27 /*******************************************************************************
28  * Main interface
29  ******************************************************************************/
30 
31 
32 mrloginparam_t* mrloginparam_new()
33 {
34  mrloginparam_t* ths = NULL;
35 
36  if( (ths=calloc(1, sizeof(mrloginparam_t)))==NULL ) {
37  exit(22); /* cannot allocate little memory, unrecoverable error */
38  }
39 
40  return ths;
41 }
42 
43 
44 void mrloginparam_unref(mrloginparam_t* ths)
45 {
46  if( ths==NULL ) {
47  return;
48  }
49 
50  mrloginparam_empty(ths);
51  free(ths);
52 }
53 
54 
55 void mrloginparam_empty(mrloginparam_t* ths)
56 {
57  if( ths == NULL ) {
58  return; /* ok, but nothing to do */
59  }
60 
61  free(ths->m_addr); ths->m_addr = NULL;
62  free(ths->m_mail_server); ths->m_mail_server = NULL;
63  ths->m_mail_port = 0;
64  free(ths->m_mail_user); ths->m_mail_user = NULL;
65  free(ths->m_mail_pw); ths->m_mail_pw = NULL;
66  free(ths->m_send_server); ths->m_send_server = NULL;
67  ths->m_send_port = 0;
68  free(ths->m_send_user); ths->m_send_user = NULL;
69  free(ths->m_send_pw); ths->m_send_pw = NULL;
70  ths->m_server_flags= 0;
71 }
72 
73 
74 void mrloginparam_read__(mrloginparam_t* ths, mrsqlite3_t* sql, const char* prefix)
75 {
76  char* key = NULL;
77  #define MR_PREFIX(a) sqlite3_free(key); key=sqlite3_mprintf("%s%s", prefix, (a));
78 
79  mrloginparam_empty(ths);
80 
81  MR_PREFIX("addr"); ths->m_addr = mrsqlite3_get_config__ (sql, key, NULL);
82 
83  MR_PREFIX("mail_server"); ths->m_mail_server = mrsqlite3_get_config__ (sql, key, NULL);
84  MR_PREFIX("mail_port"); ths->m_mail_port = mrsqlite3_get_config_int__(sql, key, 0);
85  MR_PREFIX("mail_user"); ths->m_mail_user = mrsqlite3_get_config__ (sql, key, NULL);
86  MR_PREFIX("mail_pw"); ths->m_mail_pw = mrsqlite3_get_config__ (sql, key, NULL);
87 
88  MR_PREFIX("send_server"); ths->m_send_server = mrsqlite3_get_config__ (sql, key, NULL);
89  MR_PREFIX("send_port"); ths->m_send_port = mrsqlite3_get_config_int__(sql, key, 0);
90  MR_PREFIX("send_user"); ths->m_send_user = mrsqlite3_get_config__ (sql, key, NULL);
91  MR_PREFIX("send_pw"); ths->m_send_pw = mrsqlite3_get_config__ (sql, key, NULL);
92 
93  MR_PREFIX("server_flags");ths->m_server_flags= mrsqlite3_get_config_int__(sql, key, 0);
94 
95  sqlite3_free(key);
96 }
97 
98 
99 void mrloginparam_write__(const mrloginparam_t* ths, mrsqlite3_t* sql, const char* prefix)
100 {
101  char* key = NULL;
102 
103  MR_PREFIX("addr"); mrsqlite3_set_config__ (sql, key, ths->m_addr);
104 
105  MR_PREFIX("mail_server"); mrsqlite3_set_config__ (sql, key, ths->m_mail_server);
106  MR_PREFIX("mail_port"); mrsqlite3_set_config_int__(sql, key, ths->m_mail_port);
107  MR_PREFIX("mail_user"); mrsqlite3_set_config__ (sql, key, ths->m_mail_user);
108  MR_PREFIX("mail_pw"); mrsqlite3_set_config__ (sql, key, ths->m_mail_pw);
109 
110  MR_PREFIX("send_server"); mrsqlite3_set_config__ (sql, key, ths->m_send_server);
111  MR_PREFIX("send_port"); mrsqlite3_set_config_int__(sql, key, ths->m_send_port);
112  MR_PREFIX("send_user"); mrsqlite3_set_config__ (sql, key, ths->m_send_user);
113  MR_PREFIX("send_pw"); mrsqlite3_set_config__ (sql, key, ths->m_send_pw);
114 
115  MR_PREFIX("server_flags"); mrsqlite3_set_config_int__(sql, key, ths->m_server_flags);
116 
117  sqlite3_free(key);
118 }
119 
120 
121 static char* get_readable_flags(int flags)
122 {
123  mrstrbuilder_t strbuilder;
124  mrstrbuilder_init(&strbuilder);
125  #define CAT_FLAG(f, s) if( (1<<bit)==(f) ) { mrstrbuilder_cat(&strbuilder, (s)); flag_added = 1; }
126 
127  for( int bit = 0; bit <= 30; bit++ )
128  {
129  if( flags&(1<<bit) )
130  {
131  int flag_added = 0;
132 
133  CAT_FLAG(MR_AUTH_XOAUTH2, "XOAUTH2 ");
134  CAT_FLAG(MR_AUTH_NORMAL, "AUTH_NORMAL ");
135 
136  CAT_FLAG(MR_IMAP_SOCKET_STARTTLS, "IMAP_STARTTLS ");
137  CAT_FLAG(MR_IMAP_SOCKET_SSL, "IMAP_SSL ");
138  CAT_FLAG(MR_IMAP_SOCKET_PLAIN, "IMAP_PLAIN ");
139 
140  CAT_FLAG(MR_SMTP_SOCKET_STARTTLS, "SMTP_STARTTLS ");
141  CAT_FLAG(MR_SMTP_SOCKET_SSL, "SMTP_SSL ");
142  CAT_FLAG(MR_SMTP_SOCKET_PLAIN, "SMTP_PLAIN ");
143 
144  CAT_FLAG(MR_NO_EXTRA_IMAP_UPLOAD, "NO_EXTRA_IMAP_UPLOAD ");
145  CAT_FLAG(MR_NO_MOVE_TO_CHATS, "NO_MOVE_TO_CHATS ");
146 
147  if( !flag_added ) {
148  char* temp = mr_mprintf("0x%x ", 1<<bit); mrstrbuilder_cat(&strbuilder, temp); free(temp);
149  }
150  }
151  }
152 
153  if( strbuilder.m_buf[0]==0 ) { mrstrbuilder_cat(&strbuilder, "0"); }
154  mr_trim(strbuilder.m_buf);
155  return strbuilder.m_buf;
156 }
157 
158 
159 char* mrloginparam_get_readable(const mrloginparam_t* ths)
160 {
161  const char* unset = "0";
162  const char* pw = "***";
163 
164  if( ths==NULL ) {
165  return safe_strdup(NULL);
166  }
167 
168  char* flags_readable = get_readable_flags(ths->m_server_flags);
169 
170  char* ret = mr_mprintf("%s %s:%s:%s:%i %s:%s:%s:%i %s",
171  ths->m_addr? ths->m_addr : unset,
172 
173  ths->m_mail_user? ths->m_mail_user : unset,
174  ths->m_mail_pw? pw : unset,
175  ths->m_mail_server? ths->m_mail_server : unset,
176  ths->m_mail_port,
177 
178  ths->m_send_user? ths->m_send_user : unset,
179  ths->m_send_pw? pw : unset,
180  ths->m_send_server? ths->m_send_server : unset,
181  ths->m_send_port,
182 
183  flags_readable);
184 
185  free(flags_readable);
186  return ret;
187 }
188 
- - - - diff --git a/docs/user/html/mrmailbox_8c_source.html b/docs/user/html/mrmailbox_8c_source.html deleted file mode 100644 index a3572c12..00000000 --- a/docs/user/html/mrmailbox_8c_source.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmailbox.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmailbox.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <sys/stat.h>
24 #include <sys/types.h> /* for getpid() */
25 #include <unistd.h> /* for getpid() */
26 #include <openssl/opensslv.h>
27 #include "mrmailbox_internal.h"
28 #include "mrimap.h"
29 #include "mrsmtp.h"
30 #include "mrmimeparser.h"
31 #include "mrmimefactory.h"
32 #include "mrtools.h"
33 #include "mrjob.h"
34 #include "mrloginparam.h"
35 #include "mrkey.h"
36 #include "mrpgp.h"
37 #include "mrapeerstate.h"
38 
39 
40 /*******************************************************************************
41  * Handle groups for received messages
42  ******************************************************************************/
43 
44 
45 #define MR_CREATE_GROUP_AS_NEEDED 0x01
46 
47 
48 static uint32_t lookup_group_by_grpid__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser, int create_flags,
49  uint32_t from_id, carray* to_ids)
50 {
51  /* search the grpid in the header */
52  uint32_t chat_id = 0;
53  clistiter* cur;
54  struct mailimf_field* field;
55  char* grpid1 = NULL, *grpid2 = NULL, *grpid3 = NULL, *grpid4 = NULL;
56  const char* grpid = NULL; /* must not be freed, just one of the others */
57  char* grpname = NULL;
58  sqlite3_stmt* stmt;
59  int i, to_ids_cnt = carray_count(to_ids);
60  char* self_addr = NULL;
61  int recreate_member_list = 0;
62  int send_EVENT_CHAT_MODIFIED = 0;
63 
64  /* special commands */
65  char* X_MrRemoveFromGrp = NULL; /* pointer somewhere into mime_parser, must not be freed */
66  char* X_MrAddToGrp = NULL; /* pointer somewhere into mime_parser, must not be freed */
67  int X_MrGrpNameChanged = 0;
68  int X_MrGrpImageChanged = 0;
69 
70  for( cur = clist_begin(mime_parser->m_header->fld_list); cur!=NULL ; cur=clist_next(cur) )
71  {
72  field = (struct mailimf_field*)clist_content(cur);
73  if( field )
74  {
75  if( field->fld_type == MAILIMF_FIELD_OPTIONAL_FIELD )
76  {
77  struct mailimf_optional_field* optional_field = field->fld_data.fld_optional_field;
78  if( optional_field && optional_field->fld_name ) {
79  if( strcasecmp(optional_field->fld_name, "X-MrGrpId")==0 || strcasecmp(optional_field->fld_name, "Chat-Group-ID")==0 ) {
80  grpid1 = safe_strdup(optional_field->fld_value);
81  }
82  else if( strcasecmp(optional_field->fld_name, "X-MrGrpName")==0 || strcasecmp(optional_field->fld_name, "Chat-Group-Name")==0 ) {
83  grpname = mr_decode_header_string(optional_field->fld_value); /* this is no changed groupname message */
84  }
85  else if( strcasecmp(optional_field->fld_name, "X-MrRemoveFromGrp")==0 || strcasecmp(optional_field->fld_name, "Chat-Group-Member-Removed")==0 ) {
86  X_MrRemoveFromGrp = optional_field->fld_value;
87  mime_parser->m_is_system_message = MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP;
88  }
89  else if( strcasecmp(optional_field->fld_name, "X-MrAddToGrp")==0 || strcasecmp(optional_field->fld_name, "Chat-Group-Member-Added")==0 ) {
90  X_MrAddToGrp = optional_field->fld_value;
91  mime_parser->m_is_system_message = MR_SYSTEM_MEMBER_ADDED_TO_GROUP;
92  }
93  else if( strcasecmp(optional_field->fld_name, "X-MrGrpNameChanged")==0 || strcasecmp(optional_field->fld_name, "Chat-Group-Name-Changed")==0 ) {
94  X_MrGrpNameChanged = 1;
95  mime_parser->m_is_system_message = MR_SYSTEM_GROUPNAME_CHANGED;
96  }
97  else if( strcasecmp(optional_field->fld_name, "Chat-Group-Image")==0 ) {
98  X_MrGrpImageChanged = 1;
99  mime_parser->m_is_system_message = MR_SYSTEM_GROUPIMAGE_CHANGED;
100  }
101  }
102  }
103  else if( field->fld_type == MAILIMF_FIELD_MESSAGE_ID )
104  {
105  struct mailimf_message_id* fld_message_id = field->fld_data.fld_message_id;
106  if( fld_message_id ) {
107  grpid2 = mr_extract_grpid_from_rfc724_mid(fld_message_id->mid_value);
108  }
109  }
110  else if( field->fld_type == MAILIMF_FIELD_IN_REPLY_TO )
111  {
112  struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
113  if( fld_in_reply_to ) {
114  grpid3 = mr_extract_grpid_from_rfc724_mid_list(fld_in_reply_to->mid_list);
115  }
116  }
117  else if( field->fld_type == MAILIMF_FIELD_REFERENCES )
118  {
119  struct mailimf_references* fld_references = field->fld_data.fld_references;
120  if( fld_references ) {
121  grpid4 = mr_extract_grpid_from_rfc724_mid_list(fld_references->mid_list);
122  }
123  }
124 
125  }
126  }
127 
128  grpid = grpid1? grpid1 : (grpid2? grpid2 : (grpid3? grpid3 : grpid4));
129  if( grpid == NULL ) {
130  goto cleanup;
131  }
132 
133  /* check, if we have a chat with this group ID */
134  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_CHATS_WHERE_grpid,
135  "SELECT id FROM chats WHERE grpid=?;");
136  sqlite3_bind_text (stmt, 1, grpid, -1, SQLITE_STATIC);
137  if( sqlite3_step(stmt)==SQLITE_ROW ) {
138  chat_id = sqlite3_column_int(stmt, 0);
139  }
140 
141  /* check if the sender is a member of the existing group -
142  if not, the message does not go to the group chat but to the normal chat with the sender */
143  if( chat_id!=0 && !mrmailbox_is_contact_in_chat__(mailbox, chat_id, from_id) ) {
144  chat_id = 0;
145  goto cleanup;
146  }
147 
148  /* check if the group does not exist but should be created */
149  int group_explicitly_left = mrmailbox_group_explicitly_left__(mailbox, grpid);
150 
151  self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", "");
152  if( chat_id == 0
153  && (create_flags&MR_CREATE_GROUP_AS_NEEDED)
154  && grpname
155  && X_MrRemoveFromGrp==NULL /*otherwise, a pending "quit" message may pop up*/
156  && (!group_explicitly_left || (X_MrAddToGrp&&strcasecmp(self_addr,X_MrAddToGrp)==0) ) /*re-create explicitly left groups only if ourself is re-added*/
157  )
158  {
159  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql,
160  "INSERT INTO chats (type, name, grpid) VALUES(?, ?, ?);");
161  sqlite3_bind_int (stmt, 1, MR_CHAT_TYPE_GROUP);
162  sqlite3_bind_text(stmt, 2, grpname, -1, SQLITE_STATIC);
163  sqlite3_bind_text(stmt, 3, grpid, -1, SQLITE_STATIC);
164  if( sqlite3_step(stmt)!=SQLITE_DONE ) {
165  goto cleanup;
166  }
167  sqlite3_finalize(stmt);
168  chat_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
169  recreate_member_list = 1;
170  }
171 
172  /* again, check chat_id */
173  if( chat_id <= MR_CHAT_ID_LAST_SPECIAL ) {
174  chat_id = 0;
175  if( group_explicitly_left ) {
176  chat_id = MR_CHAT_ID_TRASH; /* we got a message for a chat we've deleted - do not show this even as a normal chat */
177  }
178  goto cleanup;
179  }
180 
181  /* execute group commands */
182  if( X_MrAddToGrp || X_MrRemoveFromGrp )
183  {
184  recreate_member_list = 1;
185  }
186  else if( X_MrGrpNameChanged && grpname && strlen(grpname) < 200 )
187  {
188  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "UPDATE chats SET name=? WHERE id=?;");
189  sqlite3_bind_text(stmt, 1, grpname, -1, SQLITE_STATIC);
190  sqlite3_bind_int (stmt, 2, chat_id);
191  sqlite3_step(stmt);
192  sqlite3_finalize(stmt);
193  mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
194  }
195 
196  if( X_MrGrpImageChanged )
197  {
198  int ok = 0;
199  char* grpimage = NULL;
200  if( carray_count(mime_parser->m_parts)>=1 ) {
201  mrmimepart_t* textpart = (mrmimepart_t*)carray_get(mime_parser->m_parts, 0);
202  if( textpart->m_type == MR_MSG_TEXT ) {
203  if( carray_count(mime_parser->m_parts)>=2 ) {
204  mrmimepart_t* imgpart = (mrmimepart_t*)carray_get(mime_parser->m_parts, 1);
205  if( imgpart->m_type == MR_MSG_IMAGE ) {
206  grpimage = mrparam_get(imgpart->m_param, MRP_FILE, NULL);
207  ok = 1;
208  }
209  }
210  else {
211  ok = 1;
212  }
213  }
214  }
215 
216  if( ok ) {
217  mrchat_t* chat = mrchat_new(mailbox);
218  mrmailbox_log_info(mailbox, 0, "New group image set to %s.", grpimage? "DELETED" : grpimage);
219  mrchat_load_from_db__(chat, chat_id);
220  mrparam_set(chat->m_param, MRP_PROFILE_IMAGE, grpimage/*may be NULL*/);
221  mrchat_update_param__(chat);
222  mrchat_unref(chat);
223  free(grpimage);
224  send_EVENT_CHAT_MODIFIED = 1;
225  }
226  }
227 
228  /* add members to group/check members
229  for recreation: we should add a timestamp */
230  if( recreate_member_list )
231  {
232  const char* skip = X_MrRemoveFromGrp? X_MrRemoveFromGrp : NULL;
233 
234  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "DELETE FROM chats_contacts WHERE chat_id=?;");
235  sqlite3_bind_int (stmt, 1, chat_id);
236  sqlite3_step(stmt);
237  sqlite3_finalize(stmt);
238 
239  if( skip==NULL || strcasecmp(self_addr, skip) != 0 ) {
240  mrmailbox_add_contact_to_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF);
241  }
242 
243  if( from_id > MR_CONTACT_ID_LAST_SPECIAL ) {
244  if( mrmailbox_contact_addr_equals__(mailbox, from_id, self_addr)==0
245  && (skip==NULL || mrmailbox_contact_addr_equals__(mailbox, from_id, skip)==0) ) {
246  mrmailbox_add_contact_to_chat__(mailbox, chat_id, from_id);
247  }
248  }
249 
250  for( i = 0; i < to_ids_cnt; i++ )
251  {
252  uint32_t to_id = (uint32_t)(uintptr_t)carray_get(to_ids, i); /* to_id is only once in to_ids and is non-special */
253  if( mrmailbox_contact_addr_equals__(mailbox, to_id, self_addr)==0
254  && (skip==NULL || mrmailbox_contact_addr_equals__(mailbox, to_id, skip)==0) ) {
255  mrmailbox_add_contact_to_chat__(mailbox, chat_id, to_id);
256  }
257  }
258  send_EVENT_CHAT_MODIFIED = 1;
259  }
260 
261  if( send_EVENT_CHAT_MODIFIED ) {
262  mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
263  }
264 
265  /* check the number of receivers -
266  the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */
267  if( to_ids_cnt == 1 && mime_parser->m_is_send_by_messenger==0 ) {
268  int is_contact_cnt = mrmailbox_get_chat_contact_count__(mailbox, chat_id);
269  if( is_contact_cnt > 3 /* to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt. So everything up to 3 is no error. */ ) {
270  chat_id = 0;
271  goto cleanup;
272  }
273  }
274 
275 cleanup:
276  free(grpid1);
277  free(grpid2);
278  free(grpid3);
279  free(grpid4);
280  free(grpname);
281  free(self_addr);
282  return chat_id;
283 }
284 
285 
286 /*******************************************************************************
287  * Receive a message and add it to the database
288  ******************************************************************************/
289 
290 
291 static void receive_imf(mrmailbox_t* ths, const char* imf_raw_not_terminated, size_t imf_raw_bytes,
292  const char* server_folder, uint32_t server_uid, uint32_t flags)
293 {
294  /* the function returns the number of created messages in the database */
295  int incoming = 0;
296  int incoming_origin = MR_ORIGIN_UNSET;
297  #define outgoing (!incoming)
298 
299  carray* to_ids = NULL;
300 
301  uint32_t from_id = 0;
302  int from_id_blocked = 0;
303  uint32_t to_id = 0;
304  uint32_t chat_id = 0;
305  int state = MR_STATE_UNDEFINED;
306 
307  sqlite3_stmt* stmt;
308  size_t i, icnt;
309  uint32_t first_dblocal_id = 0;
310  char* rfc724_mid = NULL; /* Message-ID from the header */
311  time_t message_timestamp = MR_INVALID_TIMESTAMP;
312  mrmimeparser_t* mime_parser = mrmimeparser_new(ths->m_blobdir, ths);
313  int db_locked = 0;
314  int transaction_pending = 0;
315  clistiter* cur1;
316  const struct mailimf_field* field;
317 
318  carray* created_db_entries = carray_new(16);
319  int create_event_to_send = MR_EVENT_MSGS_CHANGED;
320 
321  carray* rr_event_to_send = carray_new(16);
322 
323  int has_return_path = 0;
324  char* txt_raw = NULL;
325 
326  mrmailbox_log_info(ths, 0, "Receive message #%lu from %s.", server_uid, server_folder? server_folder:"?");
327 
328  to_ids = carray_new(16);
329  if( to_ids==NULL || created_db_entries==NULL || rr_event_to_send==NULL || mime_parser == NULL ) {
330  mrmailbox_log_info(ths, 0, "Bad param.");
331  goto cleanup;
332  }
333 
334  /* parse the imf to mailimf_message {
335  mailimf_fields* msg_fields {
336  clist* fld_list; // list of mailimf_field
337  }
338  mailimf_body* msg_body { // != NULL
339  const char * bd_text; // != NULL
340  size_t bd_size;
341  }
342  };
343  normally, this is done by mailimf_message_parse(), however, as we also need the MIME data,
344  we use mailmime_parse() through MrMimeParser (both call mailimf_struct_multiple_parse() somewhen, I did not found out anything
345  that speaks against this approach yet) */
346  mrmimeparser_parse(mime_parser, imf_raw_not_terminated, imf_raw_bytes);
347  if( mime_parser->m_header == NULL ) {
348  mrmailbox_log_info(ths, 0, "No header.");
349  goto cleanup; /* Error - even adding an empty record won't help as we do not know the message ID */
350  }
351 
352  mrsqlite3_lock(ths->m_sql);
353  db_locked = 1;
354 
355  mrsqlite3_begin_transaction__(ths->m_sql);
356  transaction_pending = 1;
357 
358 
359  /* Check, if the mail comes from extern, resp. is not send by us. This is a _really_ important step
360  as messages send by us are used to validate other mail senders and receivers.
361  For this purpose, we assume, the `Return-Path:`-header is never present if the message is send by us.
362  The `Received:`-header may be another idea, however, this is also set if mails are transfered from other accounts via IMAP.
363  Using `From:` alone is no good idea, as mailboxes may use different sending-addresses - moreover, they may change over the years.
364  However, we use `From:` as an additional hint below. */
365  for( cur1 = clist_begin(mime_parser->m_header->fld_list); cur1!=NULL ; cur1=clist_next(cur1) )
366  {
367  field = (struct mailimf_field*)clist_content(cur1);
368  if( field )
369  {
370  if( field->fld_type == MAILIMF_FIELD_RETURN_PATH )
371  {
372  has_return_path = 1;
373  }
374  else if( field->fld_type == MAILIMF_FIELD_OPTIONAL_FIELD )
375  {
376  struct mailimf_optional_field* optional_field = field->fld_data.fld_optional_field;
377  if( optional_field && strcasecmp(optional_field->fld_name, "Return-Path")==0 )
378  {
379  has_return_path = 1; /* "MAILIMF_FIELD_OPTIONAL_FIELD.Return-Path" should be "MAILIMF_FIELD_RETURN_PATH", however, this is not always the case */
380  }
381  }
382  }
383  }
384 
385  if( has_return_path ) {
386  incoming = 1;
387  }
388 
389 
390  /* for incoming messages, get From: and check if it is known (for known From:'s we add the other To:/Cc:/Bcc: in the 3rd pass) */
391  if( incoming
392  && (field=mr_find_mailimf_field(mime_parser->m_header, MAILIMF_FIELD_FROM ))!=NULL )
393  {
394  struct mailimf_from* fld_from = field->fld_data.fld_from;
395  if( fld_from )
396  {
397  int check_self;
398  carray* from_list = carray_new(16);
399  mrmailbox_add_or_lookup_contacts_by_mailbox_list__(ths, fld_from->frm_mb_list, MR_ORIGIN_INCOMING_UNKNOWN_FROM, from_list, &check_self);
400  if( check_self )
401  {
402  incoming = 0; /* The `Return-Path:`-approach above works well, however, there may be messages outgoing messages which we also receive -
403  for these messages, the `Return-Path:` is set although we're the sender. To correct these cases, we add an
404  additional From: check - which, however, will not work for older From:-addresses used on the mailbox. */
405  }
406  else
407  {
408  if( carray_count(from_list)>=1 ) /* if there is no from given, from_id stays 0 which is just fine. These messages are very rare, however, we have to add the to the database (they to to the "deaddrop" chat) to avoid a re-download from the server. See also [**] */
409  {
410  from_id = (uint32_t)(uintptr_t)carray_get(from_list, 0);
411  incoming_origin = mrmailbox_get_contact_origin__(ths, from_id, &from_id_blocked);
412  }
413  }
414  carray_free(from_list);
415  }
416  }
417 
418  /* Make sure, to_ids starts with the first To:-address (Cc: and Bcc: are added in the loop below pass) */
419  if( (field=mr_find_mailimf_field(mime_parser->m_header, MAILIMF_FIELD_TO))!=NULL )
420  {
421  struct mailimf_to* fld_to = field->fld_data.fld_to; /* can be NULL */
422  if( fld_to )
423  {
424  mrmailbox_add_or_lookup_contacts_by_address_list__(ths, fld_to->to_addr_list ,
425  outgoing? MR_ORIGIN_OUTGOING_TO : (incoming_origin>=MR_ORIGIN_MIN_VERIFIED? MR_ORIGIN_INCOMING_TO : MR_ORIGIN_INCOMING_UNKNOWN_TO), to_ids, NULL);
426  }
427  }
428 
429  if( mrmimeparser_has_nonmeta(mime_parser) )
430  {
431 
432  /**********************************************************************
433  * Add parts
434  *********************************************************************/
435 
436  /* collect the rest information */
437  for( cur1 = clist_begin(mime_parser->m_header->fld_list); cur1!=NULL ; cur1=clist_next(cur1) )
438  {
439  field = (struct mailimf_field*)clist_content(cur1);
440  if( field )
441  {
442  if( field->fld_type == MAILIMF_FIELD_MESSAGE_ID )
443  {
444  struct mailimf_message_id* fld_message_id = field->fld_data.fld_message_id;
445  if( fld_message_id ) {
446  rfc724_mid = safe_strdup(fld_message_id->mid_value);
447  }
448  }
449  else if( field->fld_type == MAILIMF_FIELD_CC )
450  {
451  struct mailimf_cc* fld_cc = field->fld_data.fld_cc;
452  if( fld_cc ) {
453  mrmailbox_add_or_lookup_contacts_by_address_list__(ths, fld_cc->cc_addr_list,
454  outgoing? MR_ORIGIN_OUTGOING_CC : (incoming_origin>=MR_ORIGIN_MIN_VERIFIED? MR_ORIGIN_INCOMING_CC : MR_ORIGIN_INCOMING_UNKNOWN_CC), to_ids, NULL);
455  }
456  }
457  else if( field->fld_type == MAILIMF_FIELD_BCC )
458  {
459  struct mailimf_bcc* fld_bcc = field->fld_data.fld_bcc;
460  if( outgoing && fld_bcc ) {
461  mrmailbox_add_or_lookup_contacts_by_address_list__(ths, fld_bcc->bcc_addr_list,
462  MR_ORIGIN_OUTGOING_BCC, to_ids, NULL);
463  }
464  }
465  else if( field->fld_type == MAILIMF_FIELD_ORIG_DATE )
466  {
467  struct mailimf_orig_date* orig_date = field->fld_data.fld_orig_date;
468  if( orig_date ) {
469  message_timestamp = mr_timestamp_from_date(orig_date->dt_date_time); /* is not yet checked against bad times! */
470  }
471  }
472  }
473 
474  } /* for */
475 
476 
477  /* check if the message introduces a new chat:
478  - outgoing messages introduce a chat with the first to: address if they are send by a messenger
479  - incoming messages introduce a chat only for known contacts if they are send by a messenger
480  (of course, the user can add other chats manually later) */
481  if( incoming )
482  {
483  state = (flags&MR_IMAP_SEEN)? MR_STATE_IN_SEEN : MR_STATE_IN_FRESH;
484  to_id = MR_CONTACT_ID_SELF;
485 
486  /* test if there is a normal chat with the sender - if so, this allows us to create groups in the next step */
487  int test_normal_chat_id = mrmailbox_lookup_real_nchat_by_contact_id__(ths, from_id); /* note that the test_normal_chat_id is also used below (saves one lookup call) */
488 
489  /* check for a group chat */
490  chat_id = lookup_group_by_grpid__(ths, mime_parser, (test_normal_chat_id || incoming_origin>=MR_ORIGIN_MIN_START_NEW_NCHAT/*always false, for now*/)? MR_CREATE_GROUP_AS_NEEDED : 0, from_id, to_ids);
491  if( chat_id == 0 )
492  {
493  if( mrmimeparser_is_mailinglist_message(mime_parser) )
494  {
495  chat_id = MR_CHAT_ID_TRASH;
496  mrmailbox_log_info(ths, 0, "Message belongs to a mailing list and is ignored.");
497  /* currently we do not show mailing list messages as the would result in lots of unwanted mesages:
498  (NB: typical mailing list header: `From: sender@gmx.net To: list@address.net)
499 
500  - even if we know the sender, it does not make sense, to extract an mailing list message from the context and
501  show it in the thread
502 
503  - if we do not know the sender, it may be "known" by the is_reply_to_known_message__() function -
504  this would be even more irritating as the sender may be unknown to the user
505  (typical scenario: the users posts a message to a mailing list and an formally unknown user answers -
506  this message would pop up in Delta Chat as it is a reply to a sent message)
507 
508  "Mailing lists messages" in this sense are messages marked by List-Id or Precedence headers.
509  For the future, we might want to show mailing lists as groups.
510  NB: MR_CHAT_ID_TRASH does not remove the message on IMAP, it simply copies it to an invisible chat
511  (we have to track the message-id as otherwise the message pops up again and again) */
512  }
513  else
514  {
515  chat_id = test_normal_chat_id;
516  if( chat_id == 0 )
517  {
518  if( incoming_origin>=MR_ORIGIN_MIN_START_NEW_NCHAT/*always false, for now*/ )
519  {
520  chat_id = mrmailbox_create_or_lookup_nchat_by_contact_id__(ths, from_id);
521  }
522  else if( mrmailbox_is_reply_to_known_message__(ths, mime_parser) )
523  {
524  mrmailbox_scaleup_contact_origin__(ths, from_id, MR_ORIGIN_INCOMING_REPLY_TO);
525  //chat_id = mrmailbox_create_or_lookup_nchat_by_contact_id__(ths, from_id); -- we do not want any chat to be created implicitly. Because of the origin-scale-up, the contact requests will pop up and this should be just fine.
526  mrmailbox_log_info(ths, 0, "Message is a reply to a known message, mark sender as known.");
527  }
528  }
529  }
530 
531  if( chat_id == 0 ) {
532  chat_id = MR_CHAT_ID_DEADDROP;
533  if( state == MR_STATE_IN_FRESH ) {
534  if( incoming_origin<MR_ORIGIN_MIN_VERIFIED && mime_parser->m_is_send_by_messenger==0 ) {
535  state = MR_STATE_IN_NOTICED; /* degrade state for unknown senders and non-delta messages (the latter may be removed if we run into spam problems, currently this is fine) (noticed messages do count as being unread; therefore, the deaddrop will not popup in the chatlist) */
536  }
537  }
538  }
539  }
540  }
541  else /* outgoing */
542  {
543  state = MR_STATE_OUT_DELIVERED; /* the mail is on the IMAP server, probably it is also deliverd. We cannot recreate other states (read, error). */
544  from_id = MR_CONTACT_ID_SELF;
545  if( carray_count(to_ids) >= 1 ) {
546  to_id = (uint32_t)(uintptr_t)carray_get(to_ids, 0);
547 
548  chat_id = lookup_group_by_grpid__(ths, mime_parser, MR_CREATE_GROUP_AS_NEEDED, from_id, to_ids);
549  if( chat_id == 0 )
550  {
551  chat_id = mrmailbox_lookup_real_nchat_by_contact_id__(ths, to_id);
552  if( chat_id == 0 && mime_parser->m_is_send_by_messenger && !mrmailbox_is_contact_blocked__(ths, to_id) ) {
553  chat_id = mrmailbox_create_or_lookup_nchat_by_contact_id__(ths, to_id);
554  }
555  }
556  }
557 
558  if( chat_id == 0 ) {
559  chat_id = MR_CHAT_ID_TO_DEADDROP;
560  }
561  }
562 
563  /* correct message_timestamp, it should not be used before,
564  however, we cannot do this earlier as we need from_id to be set */
565  message_timestamp = mrmailbox_correct_bad_timestamp__(ths, chat_id, from_id, message_timestamp, (flags&MR_IMAP_SEEN)? 0 : 1 /*fresh message?*/);
566 
567  /* unarchive chat */
568  mrmailbox_unarchive_chat__(ths, chat_id);
569 
570  /* check, if the mail is already in our database - if so, there's nothing more to do
571  (we may get a mail twice eg. it it is moved between folders) */
572  if( rfc724_mid == NULL ) {
573  /* header is lacking a Message-ID - this may be the case, if the message was sent from this account and the mail client
574  the the SMTP-server set the ID (true eg. for the Webmailer used in all-inkl-KAS)
575  in these cases, we build a message ID based on some useful header fields that do never change (date, to)
576  we do not use the folder-local id, as this will change if the mail is moved to another folder. */
577  rfc724_mid = mr_create_incoming_rfc724_mid(message_timestamp, from_id, to_ids);
578  if( rfc724_mid == NULL ) {
579  mrmailbox_log_info(ths, 0, "Cannot create Message-ID.");
580  goto cleanup;
581  }
582  }
583 
584  {
585  char* old_server_folder = NULL;
586  uint32_t old_server_uid = 0;
587  if( mrmailbox_rfc724_mid_exists__(ths, rfc724_mid, &old_server_folder, &old_server_uid) ) {
588  /* The message is already added to our database; rollback. If needed, update the server_uid which may have changed if the message was moved around on the server. */
589  if( strcmp(old_server_folder, server_folder)!=0 || old_server_uid!=server_uid ) {
590  mrsqlite3_rollback__(ths->m_sql);
591  transaction_pending = 0;
592  mrmailbox_update_server_uid__(ths, rfc724_mid, server_folder, server_uid);
593  }
594  free(old_server_folder);
595  mrmailbox_log_info(ths, 0, "Message already in DB.");
596  goto cleanup;
597  }
598  }
599 
600  /* if the message is not send by a messenger, check if it sent at least a reply to a messenger message
601  (later, we move these replies to the Chats-folder) */
602  int msgrmsg = mime_parser->m_is_send_by_messenger; /* 1 or 0 for yes/no */
603  if( msgrmsg )
604  {
605  mrmailbox_log_info(ths, 0, "Message sent by another messenger (will be moved to Chats-folder).");
606  }
607  else
608  {
609  if( mrmailbox_is_reply_to_messenger_message__(ths, mime_parser) )
610  {
611  mrmailbox_log_info(ths, 0, "Message is a reply to a messenger message (will be moved to Chats-folder).");
612  msgrmsg = 2; /* 2=no, but is reply to messenger message */
613  }
614  }
615 
616  /* fine, so far. now, split the message into simple parts usable as "short messages"
617  and add them to the database (mails send by other messenger clients should result
618  into only one message; mails send by other clients may result in several messages (eg. one per attachment)) */
619  icnt = carray_count(mime_parser->m_parts); /* should be at least one - maybe empty - part */
620  for( i = 0; i < icnt; i++ )
621  {
622  mrmimepart_t* part = (mrmimepart_t*)carray_get(mime_parser->m_parts, i);
623  if( part->m_is_meta ) {
624  continue;
625  }
626 
627  if( part->m_type == MR_MSG_TEXT ) {
628  txt_raw = mr_mprintf("%s\n\n%s", mime_parser->m_subject? mime_parser->m_subject : "", part->m_msg_raw);
629  if( mime_parser->m_is_system_message ) {
630  mrparam_set_int(part->m_param, MRP_SYSTEM_CMD, mime_parser->m_is_system_message);
631  }
632  }
633 
634  stmt = mrsqlite3_predefine__(ths->m_sql, INSERT_INTO_msgs_msscftttsmttpb,
635  "INSERT INTO msgs (rfc724_mid,server_folder,server_uid,chat_id,from_id, to_id,timestamp,type, state,msgrmsg,txt,txt_raw,param,bytes)"
636  " VALUES (?,?,?,?,?, ?,?,?, ?,?,?,?,?,?);");
637  sqlite3_bind_text (stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
638  sqlite3_bind_text (stmt, 2, server_folder, -1, SQLITE_STATIC);
639  sqlite3_bind_int (stmt, 3, server_uid);
640  sqlite3_bind_int (stmt, 4, chat_id);
641  sqlite3_bind_int (stmt, 5, from_id);
642  sqlite3_bind_int (stmt, 6, to_id);
643  sqlite3_bind_int64(stmt, 7, message_timestamp);
644  sqlite3_bind_int (stmt, 8, part->m_type);
645  sqlite3_bind_int (stmt, 9, state);
646  sqlite3_bind_int (stmt, 10, msgrmsg);
647  sqlite3_bind_text (stmt, 11, part->m_msg? part->m_msg : "", -1, SQLITE_STATIC);
648  sqlite3_bind_text (stmt, 12, txt_raw? txt_raw : "", -1, SQLITE_STATIC);
649  sqlite3_bind_text (stmt, 13, part->m_param->m_packed, -1, SQLITE_STATIC);
650  sqlite3_bind_int (stmt, 14, part->m_bytes);
651  if( sqlite3_step(stmt) != SQLITE_DONE ) {
652  mrmailbox_log_info(ths, 0, "Cannot write DB.");
653  goto cleanup; /* i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record */
654  }
655 
656  free(txt_raw);
657  txt_raw = NULL;
658 
659  if( first_dblocal_id == 0 ) {
660  first_dblocal_id = sqlite3_last_insert_rowid(ths->m_sql->m_cobj);
661  }
662 
663  carray_add(created_db_entries, (void*)(uintptr_t)chat_id, NULL);
664  carray_add(created_db_entries, (void*)(uintptr_t)first_dblocal_id, NULL);
665  }
666 
667  /* check event to send */
668  if( chat_id == MR_CHAT_ID_TRASH )
669  {
670  create_event_to_send = 0;
671  }
672  else if( incoming && state==MR_STATE_IN_FRESH )
673  {
674  if( from_id_blocked ) {
675  create_event_to_send = 0;
676  }
677  else if( chat_id == MR_CHAT_ID_DEADDROP ) {
678  create_event_to_send = MR_EVENT_MSGS_CHANGED;
679  /*if( mrsqlite3_get_config_int__(ths->m_sql, "show_deaddrop", 0)!=0 ) {
680  create_event_to_send = MR_EVENT_INCOMING_MSG;
681  }*/
682  }
683  else {
684  create_event_to_send = MR_EVENT_INCOMING_MSG;
685  }
686  }
687 
688  }
689 
690 
691  if( carray_count(mime_parser->m_reports) > 0 )
692  {
693  /******************************************************************
694  * Handle reports (mainly MDNs)
695  *****************************************************************/
696 
697  int mdns_enabled = mrsqlite3_get_config_int__(ths->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED);
698  icnt = carray_count(mime_parser->m_reports);
699  for( i = 0; i < icnt; i++ )
700  {
701  int mdn_consumed = 0;
702  struct mailmime* report_root = carray_get(mime_parser->m_reports, i);
703  struct mailmime_parameter* report_type = mr_find_ct_parameter(report_root, "report-type");
704  if( report_root==NULL || report_type==NULL || report_type->pa_value==NULL ) {
705  continue;
706  }
707 
708  if( strcmp(report_type->pa_value, "disposition-notification") == 0
709  && clist_count(report_root->mm_data.mm_multipart.mm_mp_list) >= 2 /* the first part is for humans, the second for machines */ )
710  {
711  if( mdns_enabled /*to get a clear functionality, do not show incoming MDNs if the options is disabled*/ )
712  {
713  struct mailmime* report_data = (struct mailmime*)clist_content(clist_next(clist_begin(report_root->mm_data.mm_multipart.mm_mp_list)));
714  if( report_data
715  && report_data->mm_content_type->ct_type->tp_type==MAILMIME_TYPE_COMPOSITE_TYPE
716  && report_data->mm_content_type->ct_type->tp_data.tp_composite_type->ct_type==MAILMIME_COMPOSITE_TYPE_MESSAGE
717  && strcmp(report_data->mm_content_type->ct_subtype, "disposition-notification")==0 )
718  {
719  /* we received a MDN (although the MDN is only a header, we parse it as a complete mail) */
720  const char* report_body = NULL;
721  size_t report_body_bytes = 0;
722  char* to_mmap_string_unref = NULL;
723  if( mr_mime_transfer_decode(report_data, &report_body, &report_body_bytes, &to_mmap_string_unref) )
724  {
725  struct mailmime* report_parsed = NULL;
726  size_t dummy = 0;
727  if( mailmime_parse(report_body, report_body_bytes, &dummy, &report_parsed)==MAIL_NO_ERROR
728  && report_parsed!=NULL )
729  {
730  struct mailimf_fields* report_fields = mr_find_mailimf_fields(report_parsed);
731  if( report_fields )
732  {
733  struct mailimf_optional_field* of_disposition = mr_find_mailimf_field2(report_fields, "Disposition"); /* MUST be preset, _if_ preset, we assume a sort of attribution and do not go into details */
734  struct mailimf_optional_field* of_org_msgid = mr_find_mailimf_field2(report_fields, "Original-Message-ID"); /* can't live without */
735  if( of_disposition && of_disposition->fld_value && of_org_msgid && of_org_msgid->fld_value )
736  {
737  char* rfc724_mid = NULL;
738  dummy = 0;
739  if( mailimf_msg_id_parse(of_org_msgid->fld_value, strlen(of_org_msgid->fld_value), &dummy, &rfc724_mid)==MAIL_NO_ERROR
740  && rfc724_mid!=NULL )
741  {
742  uint32_t chat_id = 0;
743  uint32_t msg_id = 0;
744  if( mrmailbox_mdn_from_ext__(ths, from_id, rfc724_mid, &chat_id, &msg_id) ) {
745  carray_add(rr_event_to_send, (void*)(uintptr_t)chat_id, NULL);
746  carray_add(rr_event_to_send, (void*)(uintptr_t)msg_id, NULL);
747  }
748  mdn_consumed = (msg_id!=0);
749  free(rfc724_mid);
750  }
751  }
752  }
753  mailmime_free(report_parsed);
754  }
755 
756  if( to_mmap_string_unref ) { mmap_string_unref(to_mmap_string_unref); }
757  }
758  }
759  }
760 
761  /* Move the MDN away to the chats folder. We do this for:
762  - Consumed or not consumed MDNs from other messengers
763  - Consumed MDNs from normal MUAs
764  Unconsumed MDNs from normal MUAs are _not_ moved.
765  NB: we do not delete the MDN as it may be used by other clients
766 
767  CAVE: we rely on mrimap_markseen_msg() not to move messages that are aready in the correct folder.
768  otherwiese, the moved message get a new server_uid and is "fresh" again and we will be here again to move it away -
769  a classical deadlock, see also (***) */
770  if( mime_parser->m_is_send_by_messenger || mdn_consumed ) {
771  char* jobparam = mr_mprintf("%c=%s\n%c=%lu", MRP_SERVER_FOLDER, server_folder, MRP_SERVER_UID, server_uid);
772  mrjob_add__(ths, MRJ_MARKSEEN_MDN_ON_IMAP, 0, jobparam);
773  free(jobparam);
774  }
775  }
776 
777  } /* for() */
778 
779  }
780 
781  /* debug print? */
782  if( mrsqlite3_get_config_int__(ths->m_sql, "save_eml", 0) ) {
783  char* emlname = mr_mprintf("%s/%s-%i.eml", ths->m_blobdir, server_folder, (int)first_dblocal_id /*may be 0 for MDNs*/);
784  FILE* emlfileob = fopen(emlname, "w");
785  if( emlfileob ) {
786  fwrite(imf_raw_not_terminated, 1, imf_raw_bytes, emlfileob);
787  fclose(emlfileob);
788  }
789  free(emlname);
790  }
791 
792  /* end sql-transaction */
793  mrsqlite3_commit__(ths->m_sql);
794  transaction_pending = 0;
795 
796  /* done */
797 cleanup:
798  if( transaction_pending ) {
799  mrsqlite3_rollback__(ths->m_sql);
800  }
801 
802  if( db_locked ) {
803  mrsqlite3_unlock(ths->m_sql);
804  }
805 
806  if( mime_parser ) {
807  mrmimeparser_unref(mime_parser);
808  }
809 
810  if( rfc724_mid ) {
811  free(rfc724_mid);
812  }
813 
814  if( to_ids ) {
815  carray_free(to_ids);
816  }
817 
818  if( created_db_entries ) {
819  if( create_event_to_send ) {
820  size_t i, icnt = carray_count(created_db_entries);
821  for( i = 0; i < icnt; i += 2 ) {
822  ths->m_cb(ths, create_event_to_send, (uintptr_t)carray_get(created_db_entries, i), (uintptr_t)carray_get(created_db_entries, i+1));
823  }
824  }
825  carray_free(created_db_entries);
826  }
827 
828  if( rr_event_to_send ) {
829  size_t i, icnt = carray_count(rr_event_to_send);
830  for( i = 0; i < icnt; i += 2 ) {
831  ths->m_cb(ths, MR_EVENT_MSG_READ, (uintptr_t)carray_get(rr_event_to_send, i), (uintptr_t)carray_get(rr_event_to_send, i+1));
832  }
833  carray_free(rr_event_to_send);
834  }
835 
836  free(txt_raw);
837 }
838 
839 
840 /*******************************************************************************
841  * Main interface
842  ******************************************************************************/
843 
844 
845 static uintptr_t cb_dummy(mrmailbox_t* mailbox, int event, uintptr_t data1, uintptr_t data2)
846 {
847  return 0;
848 }
849 static int32_t cb_get_config_int(mrimap_t* imap, const char* key, int32_t value)
850 {
851  mrmailbox_t* mailbox = (mrmailbox_t*)imap->m_userData;
852  mrsqlite3_lock(mailbox->m_sql);
853  int32_t ret = mrsqlite3_get_config_int__(mailbox->m_sql, key, value);
854  mrsqlite3_unlock(mailbox->m_sql);
855  return ret;
856 }
857 static void cb_set_config_int(mrimap_t* imap, const char* key, int32_t def)
858 {
859  mrmailbox_t* mailbox = (mrmailbox_t*)imap->m_userData;
860  mrsqlite3_lock(mailbox->m_sql);
861  mrsqlite3_set_config_int__(mailbox->m_sql, key, def);
862  mrsqlite3_unlock(mailbox->m_sql);
863 }
864 static void cb_receive_imf(mrimap_t* imap, const char* imf_raw_not_terminated, size_t imf_raw_bytes, const char* server_folder, uint32_t server_uid, uint32_t flags)
865 {
866  mrmailbox_t* mailbox = (mrmailbox_t*)imap->m_userData;
867  receive_imf(mailbox, imf_raw_not_terminated, imf_raw_bytes, server_folder, server_uid, flags);
868 }
869 
870 
897 mrmailbox_t* mrmailbox_new(mrmailboxcb_t cb, void* userdata, const char* os_name)
898 {
899  mrmailbox_get_thread_index(); /* make sure, the main thread has the index #1, only for a nicer look of the logs */
900 
901  mrmailbox_t* ths = NULL;
902 
903  if( (ths=calloc(1, sizeof(mrmailbox_t)))==NULL ) {
904  exit(23); /* cannot allocate little memory, unrecoverable error */
905  }
906 
907  pthread_mutex_init(&ths->m_log_ringbuf_critical, NULL);
908 
909  pthread_mutex_init(&ths->m_wake_lock_critical, NULL);
910 
911  ths->m_sql = mrsqlite3_new(ths);
912  ths->m_cb = cb? cb : cb_dummy;
913  ths->m_userdata = userdata;
914  ths->m_imap = mrimap_new(cb_get_config_int, cb_set_config_int, cb_receive_imf, (void*)ths, ths);
915  ths->m_smtp = mrsmtp_new(ths);
916  ths->m_os_name = safe_strdup(os_name);
917 
918  mrjob_init_thread(ths);
919 
920  mrpgp_init(ths);
921 
922  /* Random-seed. An additional seed with more random data is done just before key generation
923  (the timespan between this call and the key generation time is typically random.
924  Moreover, later, we add a hash of the first message data to the random-seed
925  (it would be okay to seed with even more sensible data, the seed values cannot be recovered from the PRNG output, see OpenSSL's RAND_seed() ) */
926  {
927  uintptr_t seed[5];
928  seed[0] = (uintptr_t)time(NULL); /* time */
929  seed[1] = (uintptr_t)seed; /* stack */
930  seed[2] = (uintptr_t)ths; /* heap */
931  seed[3] = (uintptr_t)pthread_self(); /* thread ID */
932  seed[4] = (uintptr_t)getpid(); /* process ID */
933  mrpgp_rand_seed(ths, seed, sizeof(seed));
934  }
935 
936  if( s_localize_mb_obj==NULL ) {
937  s_localize_mb_obj = ths;
938  }
939 
940  return ths;
941 }
942 
943 
955 {
956  if( mailbox==NULL ) {
957  return;
958  }
959 
960  mrpgp_exit(mailbox);
961 
962  mrjob_exit_thread(mailbox);
963 
964  if( mrmailbox_is_open(mailbox) ) {
965  mrmailbox_close(mailbox);
966  }
967 
968  mrimap_unref(mailbox->m_imap);
969  mrsmtp_unref(mailbox->m_smtp);
970  mrsqlite3_unref(mailbox->m_sql);
971  pthread_mutex_destroy(&mailbox->m_wake_lock_critical);
972 
973  pthread_mutex_destroy(&mailbox->m_log_ringbuf_critical);
974  for( int i = 0; i < MR_LOG_RINGBUF_SIZE; i++ ) {
975  free(mailbox->m_log_ringbuf[i]);
976  }
977 
978  free(mailbox->m_os_name);
979  free(mailbox);
980 
981  if( s_localize_mb_obj==mailbox ) {
982  s_localize_mb_obj = NULL;
983  }
984 }
985 
986 
987 static void update_config_cache__(mrmailbox_t* ths, const char* key)
988 {
989  if( key==NULL || strcmp(key, "e2ee_enabled")==0 ) {
990  ths->m_e2ee_enabled = mrsqlite3_get_config_int__(ths->m_sql, "e2ee_enabled", MR_E2EE_DEFAULT_ENABLED);
991  }
992 }
993 
994 
1010 int mrmailbox_open(mrmailbox_t* mailbox, const char* dbfile, const char* blobdir)
1011 {
1012  int success = 0;
1013  int db_locked = 0;
1014 
1015  if( mailbox == NULL || dbfile == NULL ) {
1016  goto cleanup;
1017  }
1018 
1019  mrsqlite3_lock(mailbox->m_sql);
1020  db_locked = 1;
1021 
1022  /* Open() sets up the object and connects to the given database
1023  from which all configuration is read/written to. */
1024 
1025  /* Create/open sqlite database */
1026  if( !mrsqlite3_open__(mailbox->m_sql, dbfile, 0) ) {
1027  goto cleanup;
1028  }
1029  mrjob_kill_action__(mailbox, MRJ_CONNECT_TO_IMAP);
1030 
1031  /* backup dbfile name */
1032  mailbox->m_dbfile = safe_strdup(dbfile);
1033 
1034  /* set blob-directory
1035  (to avoid double slashed, the given directory should not end with an slash) */
1036  if( blobdir && blobdir[0] ) {
1037  mailbox->m_blobdir = safe_strdup(blobdir);
1038  }
1039  else {
1040  mailbox->m_blobdir = mr_mprintf("%s-blobs", dbfile);
1041  mr_create_folder(mailbox->m_blobdir, mailbox);
1042  }
1043 
1044  /* cache some settings */
1045  update_config_cache__(mailbox, NULL);
1046 
1047  /* success */
1048  success = 1;
1049 
1050  /* cleanup */
1051 cleanup:
1052  if( !success ) {
1053  if( mrsqlite3_is_open(mailbox->m_sql) ) {
1054  mrsqlite3_close__(mailbox->m_sql);
1055  }
1056  }
1057 
1058  if( db_locked ) {
1059  mrsqlite3_unlock(mailbox->m_sql);
1060  }
1061 
1062  return success;
1063 }
1064 
1065 
1076 {
1077  if( mailbox == NULL ) {
1078  return;
1079  }
1080 
1081  mrimap_disconnect(mailbox->m_imap);
1082  mrsmtp_disconnect(mailbox->m_smtp);
1083 
1084  mrsqlite3_lock(mailbox->m_sql);
1085 
1086  if( mrsqlite3_is_open(mailbox->m_sql) ) {
1087  mrsqlite3_close__(mailbox->m_sql);
1088  }
1089 
1090  free(mailbox->m_dbfile);
1091  mailbox->m_dbfile = NULL;
1092 
1093  free(mailbox->m_blobdir);
1094  mailbox->m_blobdir = NULL;
1095 
1096  mrsqlite3_unlock(mailbox->m_sql);
1097 }
1098 
1099 
1109 int mrmailbox_is_open(const mrmailbox_t* mailbox)
1110 {
1111  if( mailbox == NULL ) {
1112  return 0; /* error - database not opened */
1113  }
1114 
1115  return mrsqlite3_is_open(mailbox->m_sql);
1116 }
1117 
1118 
1119 int mrmailbox_poke_eml_file(mrmailbox_t* ths, const char* filename)
1120 {
1121  /* mainly for testing, may be called by mrmailbox_import_spec() */
1122  int success = 0;
1123  char* data = NULL;
1124  size_t data_bytes;
1125 
1126  if( ths == NULL ) {
1127  return 0;
1128  }
1129 
1130  if( mr_read_file(filename, (void**)&data, &data_bytes, ths) == 0 ) {
1131  goto cleanup;
1132  }
1133 
1134  receive_imf(ths, data, data_bytes, "import", 0, 0); /* this static function is the reason why this function is not moved to mrmailbox_imex.c */
1135  success = 1;
1136 
1137 cleanup:
1138  free(data);
1139 
1140  return success;
1141 }
1142 
1143 
1144 /*******************************************************************************
1145  * INI-handling, Information
1146  ******************************************************************************/
1147 
1148 
1176 int mrmailbox_set_config(mrmailbox_t* ths, const char* key, const char* value)
1177 {
1178  int ret;
1179 
1180  if( ths == NULL || key == NULL ) { /* "value" may be NULL */
1181  return 0;
1182  }
1183 
1184  mrsqlite3_lock(ths->m_sql);
1185  ret = mrsqlite3_set_config__(ths->m_sql, key, value);
1186  update_config_cache__(ths, key);
1187  mrsqlite3_unlock(ths->m_sql);
1188 
1189  return ret;
1190 }
1191 
1192 
1207 char* mrmailbox_get_config(mrmailbox_t* ths, const char* key, const char* def)
1208 {
1209  char* ret;
1210 
1211  if( ths == NULL || key == NULL ) { /* "def" may be NULL */
1212  return strdup_keep_null(def);
1213  }
1214 
1215  mrsqlite3_lock(ths->m_sql);
1216  ret = mrsqlite3_get_config__(ths->m_sql, key, def);
1217  mrsqlite3_unlock(ths->m_sql);
1218 
1219  return ret; /* the returned string must be free()'d, returns NULL only if "def" is NULL and "key" is unset */
1220 }
1221 
1222 
1229 int mrmailbox_set_config_int(mrmailbox_t* ths, const char* key, int32_t value)
1230 {
1231  int ret;
1232 
1233  if( ths == NULL || key == NULL ) {
1234  return 0;
1235  }
1236 
1237  mrsqlite3_lock(ths->m_sql);
1238  ret = mrsqlite3_set_config_int__(ths->m_sql, key, value);
1239  update_config_cache__(ths, key);
1240  mrsqlite3_unlock(ths->m_sql);
1241 
1242  return ret;
1243 }
1244 
1245 
1251 int32_t mrmailbox_get_config_int(mrmailbox_t* ths, const char* key, int32_t def)
1252 {
1253  int32_t ret;
1254 
1255  if( ths == NULL || key == NULL ) {
1256  return def;
1257  }
1258 
1259  mrsqlite3_lock(ths->m_sql);
1260  ret = mrsqlite3_get_config_int__(ths->m_sql, key, def);
1261  mrsqlite3_unlock(ths->m_sql);
1262 
1263  return ret;
1264 }
1265 
1266 
1277 {
1278  return safe_strdup(mailbox? mailbox->m_blobdir : NULL);
1279 }
1280 
1281 
1293 {
1294  const char* unset = "0";
1295  char *displayname = NULL, *temp = NULL, *l_readable_str = NULL, *l2_readable_str = NULL, *fingerprint_str = NULL;
1296  mrloginparam_t *l = NULL, *l2 = NULL;
1297  int contacts, chats, real_msgs, deaddrop_msgs, is_configured, dbversion, mdns_enabled, e2ee_enabled, prv_key_count, pub_key_count;
1298  mrkey_t* self_public = mrkey_new();
1299 
1300  mrstrbuilder_t ret;
1301  mrstrbuilder_init(&ret);
1302 
1303  if( mailbox == NULL ) {
1304  return safe_strdup("ErrBadPtr");
1305  }
1306 
1307  /* read data (all pointers may be NULL!) */
1308  l = mrloginparam_new();
1309  l2 = mrloginparam_new();
1310 
1311  mrsqlite3_lock(mailbox->m_sql);
1312 
1313  mrloginparam_read__(l, mailbox->m_sql, "");
1314  mrloginparam_read__(l2, mailbox->m_sql, "configured_" /*the trailing underscore is correct*/);
1315 
1316  displayname = mrsqlite3_get_config__(mailbox->m_sql, "displayname", NULL);
1317 
1318  chats = mrmailbox_get_chat_cnt__(mailbox);
1319  real_msgs = mrmailbox_get_real_msg_cnt__(mailbox);
1320  deaddrop_msgs = mrmailbox_get_deaddrop_msg_cnt__(mailbox);
1321  contacts = mrmailbox_get_real_contact_cnt__(mailbox);
1322 
1323  is_configured = mrsqlite3_get_config_int__(mailbox->m_sql, "configured", 0);
1324 
1325  dbversion = mrsqlite3_get_config_int__(mailbox->m_sql, "dbversion", 0);
1326 
1327  e2ee_enabled = mailbox->m_e2ee_enabled;
1328 
1329  mdns_enabled = mrsqlite3_get_config_int__(mailbox->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED);
1330 
1331  sqlite3_stmt* stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "SELECT COUNT(*) FROM keypairs;");
1332  sqlite3_step(stmt);
1333  prv_key_count = sqlite3_column_int(stmt, 0);
1334  sqlite3_finalize(stmt);
1335 
1336  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "SELECT COUNT(*) FROM acpeerstates;");
1337  sqlite3_step(stmt);
1338  pub_key_count = sqlite3_column_int(stmt, 0);
1339  sqlite3_finalize(stmt);
1340 
1341  if( mrkey_load_self_public__(self_public, l2->m_addr, mailbox->m_sql) ) {
1342  fingerprint_str = mrkey_render_fingerprint(self_public, mailbox);
1343  }
1344  else {
1345  fingerprint_str = safe_strdup("<Not yet calculated>");
1346  }
1347 
1348  mrsqlite3_unlock(mailbox->m_sql);
1349 
1350  l_readable_str = mrloginparam_get_readable(l);
1351  l2_readable_str = mrloginparam_get_readable(l2);
1352 
1353  /* create info
1354  - some keys are display lower case - these can be changed using the `set`-command
1355  - we do not display the password here; in the cli-utility, you can see it using `get mail_pw`
1356  - use neutral speach; the Delta Chat Core is not directly related to any front end or end-product
1357  - contributors: You're welcome to add your names here */
1358  temp = mr_mprintf(
1359  "Chats: %i\n"
1360  "Chat messages: %i\n"
1361  "Messages in mailbox: %i\n"
1362  "Contacts: %i\n"
1363  "Database=%s, dbversion=%i, Blobdir=%s\n"
1364  "\n"
1365  "displayname=%s\n"
1366  "configured=%i\n"
1367  "config0=%s\n"
1368  "config1=%s\n"
1369  "mdns_enabled=%i\n"
1370  "e2ee_enabled=%i\n"
1371  "E2EE_DEFAULT_ENABLED=%i\n"
1372  "Private keys=%i, public keys=%i, fingerprint=\n%s\n"
1373  "\n"
1374  "Using Delta Chat Core v%i.%i.%i, SQLite %s-ts%i, libEtPan %i.%i, OpenSSL %i.%i.%i%c. Compiled " __DATE__ ", " __TIME__ " for %i bit usage.\n\n"
1375  "Log excerpt:\n"
1376  /* In the frontends, additional software hints may follow here. */
1377 
1378  , chats, real_msgs, deaddrop_msgs, contacts
1379  , mailbox->m_dbfile? mailbox->m_dbfile : unset, dbversion, mailbox->m_blobdir? mailbox->m_blobdir : unset
1380 
1381  , displayname? displayname : unset
1382  , is_configured
1383  , l_readable_str, l2_readable_str
1384 
1385  , mdns_enabled
1386 
1387  , e2ee_enabled
1388  , MR_E2EE_DEFAULT_ENABLED
1389  , prv_key_count, pub_key_count, fingerprint_str
1390 
1391  , MR_VERSION_MAJOR, MR_VERSION_MINOR, MR_VERSION_REVISION
1392  , SQLITE_VERSION, sqlite3_threadsafe() , libetpan_get_version_major(), libetpan_get_version_minor()
1393  , (int)(OPENSSL_VERSION_NUMBER>>28), (int)(OPENSSL_VERSION_NUMBER>>20)&0xFF, (int)(OPENSSL_VERSION_NUMBER>>12)&0xFF, (char)('a'-1+((OPENSSL_VERSION_NUMBER>>4)&0xFF))
1394  , sizeof(void*)*8
1395 
1396  );
1397  mrstrbuilder_cat(&ret, temp);
1398  free(temp);
1399 
1400  /* add log excerpt */
1401  pthread_mutex_lock(&mailbox->m_log_ringbuf_critical); /*take care not to log here! */
1402  for( int i = 0; i < MR_LOG_RINGBUF_SIZE; i++ ) {
1403  int j = (mailbox->m_log_ringbuf_pos+i) % MR_LOG_RINGBUF_SIZE;
1404  if( mailbox->m_log_ringbuf[j] ) {
1405  struct tm wanted_struct;
1406  memcpy(&wanted_struct, localtime(&mailbox->m_log_ringbuf_times[j]), sizeof(struct tm));
1407  temp = mr_mprintf("\n%02i:%02i:%02i ", (int)wanted_struct.tm_hour, (int)wanted_struct.tm_min, (int)wanted_struct.tm_sec);
1408  mrstrbuilder_cat(&ret, temp);
1409  mrstrbuilder_cat(&ret, mailbox->m_log_ringbuf[j]);
1410  free(temp);
1411  }
1412  }
1413  pthread_mutex_unlock(&mailbox->m_log_ringbuf_critical);
1414 
1415  /* free data */
1416  mrloginparam_unref(l);
1417  mrloginparam_unref(l2);
1418  free(displayname);
1419  free(l_readable_str);
1420  free(l2_readable_str);
1421  free(fingerprint_str);
1422  mrkey_unref(self_public);
1423  return ret.m_buf; /* must be freed by the caller */
1424 }
1425 
1426 
1427 /*******************************************************************************
1428  * Misc.
1429  ******************************************************************************/
1430 
1431 
1432 int mrmailbox_get_archived_count__(mrmailbox_t* mailbox)
1433 {
1434  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_chats_WHERE_archived, "SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;");
1435  if( sqlite3_step(stmt) == SQLITE_ROW ) {
1436  return sqlite3_column_int(stmt, 0);
1437  }
1438  return 0;
1439 }
1440 
1441 
1442 int mrmailbox_reset_tables(mrmailbox_t* ths, int bits)
1443 {
1444  mrmailbox_log_info(ths, 0, "Resetting tables (%i)...", bits);
1445 
1446  mrsqlite3_lock(ths->m_sql);
1447 
1448  if( bits & 1 ) {
1449  mrsqlite3_execute__(ths->m_sql, "DELETE FROM jobs;");
1450  mrmailbox_log_info(ths, 0, "Job resetted.");
1451  }
1452 
1453  if( bits & 2 ) {
1454  mrsqlite3_execute__(ths->m_sql, "DELETE FROM acpeerstates;");
1455  mrmailbox_log_info(ths, 0, "Peerstates resetted.");
1456  }
1457 
1458  if( bits & 4 ) {
1459  mrsqlite3_execute__(ths->m_sql, "DELETE FROM keypairs;");
1460  mrmailbox_log_info(ths, 0, "Private keypairs resetted.");
1461  }
1462 
1463  if( bits & 8 ) {
1464  mrsqlite3_execute__(ths->m_sql, "DELETE FROM contacts WHERE id>" MR_STRINGIFY(MR_CONTACT_ID_LAST_SPECIAL) ";"); /* the other IDs are reserved - leave these rows to make sure, the IDs are not used by normal contacts*/
1465  mrsqlite3_execute__(ths->m_sql, "DELETE FROM chats WHERE id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) ";");
1466  mrsqlite3_execute__(ths->m_sql, "DELETE FROM chats_contacts;");
1467  mrsqlite3_execute__(ths->m_sql, "DELETE FROM msgs WHERE id>" MR_STRINGIFY(MR_MSG_ID_LAST_SPECIAL) ";");
1468  mrsqlite3_execute__(ths->m_sql, "DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';");
1469  mrsqlite3_execute__(ths->m_sql, "DELETE FROM leftgrps;");
1470  mrmailbox_log_info(ths, 0, "Rest but server config resetted.");
1471  }
1472 
1473  update_config_cache__(ths, NULL);
1474 
1475  mrsqlite3_unlock(ths->m_sql);
1476 
1477  ths->m_cb(ths, MR_EVENT_MSGS_CHANGED, 0, 0);
1478 
1479  return 1;
1480 }
1481 
1482 
1491 {
1492  return mr_mprintf("%i.%i.%i", (int)MR_VERSION_MAJOR, (int)MR_VERSION_MINOR, (int)MR_VERSION_REVISION);
1493 }
1494 
1495 
1496 void mrmailbox_wake_lock(mrmailbox_t* mailbox)
1497 {
1498  if( mailbox == NULL ) {
1499  return;
1500  }
1501  pthread_mutex_lock(&mailbox->m_wake_lock_critical);
1502  mailbox->m_wake_lock++;
1503  if( mailbox->m_wake_lock == 1 ) {
1504  mailbox->m_cb(mailbox, MR_EVENT_WAKE_LOCK, 1, 0);
1505  }
1506  pthread_mutex_unlock(&mailbox->m_wake_lock_critical);
1507 }
1508 
1509 
1510 void mrmailbox_wake_unlock(mrmailbox_t* mailbox)
1511 {
1512  if( mailbox == NULL ) {
1513  return;
1514  }
1515  pthread_mutex_lock(&mailbox->m_wake_lock_critical);
1516  if( mailbox->m_wake_lock == 1 ) {
1517  mailbox->m_cb(mailbox, MR_EVENT_WAKE_LOCK, 0, 0);
1518  }
1519  mailbox->m_wake_lock--;
1520  pthread_mutex_unlock(&mailbox->m_wake_lock_critical);
1521 }
1522 
1523 
1524 /*******************************************************************************
1525  * Connect
1526  ******************************************************************************/
1527 
1528 
1529 void mrmailbox_connect_to_imap(mrmailbox_t* ths, mrjob_t* job /*may be NULL if the function is called directly!*/)
1530 {
1531  int is_locked = 0;
1532  mrloginparam_t* param = mrloginparam_new();
1533 
1534  if( mrimap_is_connected(ths->m_imap) ) {
1535  mrmailbox_log_info(ths, 0, "Already connected or trying to connect.");
1536  goto cleanup;
1537  }
1538 
1539  mrsqlite3_lock(ths->m_sql);
1540  is_locked = 1;
1541 
1542  if( mrsqlite3_get_config_int__(ths->m_sql, "configured", 0) == 0 ) {
1543  mrmailbox_log_error(ths, 0, "Not configured.");
1544  goto cleanup;
1545  }
1546 
1547  mrloginparam_read__(param, ths->m_sql, "configured_" /*the trailing underscore is correct*/);
1548 
1549  mrsqlite3_unlock(ths->m_sql);
1550  is_locked = 0;
1551 
1552  if( !mrimap_connect(ths->m_imap, param) ) {
1553  mrjob_try_again_later(job, MR_STANDARD_DELAY);
1554  goto cleanup;
1555  }
1556 
1557 cleanup:
1558  if( param ) {
1559  mrloginparam_unref(param);
1560  }
1561 
1562  if( is_locked ) {
1563  mrsqlite3_unlock(ths->m_sql);
1564  }
1565 }
1566 
1567 
1579 {
1580  if( mailbox == NULL ) {
1581  return;
1582  }
1583 
1584  mrsqlite3_lock(mailbox->m_sql);
1585 
1586  mailbox->m_smtp->m_log_connect_errors = 1;
1587  mailbox->m_imap->m_log_connect_errors = 1;
1588 
1589  mrjob_kill_action__(mailbox, MRJ_CONNECT_TO_IMAP);
1590  mrjob_add__(mailbox, MRJ_CONNECT_TO_IMAP, 0, NULL);
1591 
1592  mrsqlite3_unlock(mailbox->m_sql);
1593 }
1594 
1595 
1606 {
1607  if( mailbox == NULL ) {
1608  return;
1609  }
1610 
1611  mrsqlite3_lock(mailbox->m_sql);
1612  mrjob_kill_action__(mailbox, MRJ_CONNECT_TO_IMAP);
1613  mrsqlite3_unlock(mailbox->m_sql);
1614 
1615  mrimap_disconnect(mailbox->m_imap);
1616  mrsmtp_disconnect(mailbox->m_smtp);
1617 }
1618 
1619 
1620 /* restore old data from the IMAP server, not really implemented. */
1621 int mrmailbox_restore(mrmailbox_t* ths, time_t seconds_to_restore)
1622 {
1623  if( ths == NULL ) {
1624  return 0;
1625  }
1626 
1627  return mrimap_restore(ths->m_imap, seconds_to_restore);
1628 }
1629 
1630 
1631 void mrmailbox_heartbeat(mrmailbox_t* ths)
1632 {
1633  if( ths == NULL ) {
1634  return;
1635  }
1636 
1637  //mrmailbox_log_info(ths, 0, "<3 Mailbox");
1638  mrimap_heartbeat(ths->m_imap);
1639 }
1640 
1663 mrchatlist_t* mrmailbox_get_chatlist(mrmailbox_t* mailbox, int listflags, const char* query)
1664 {
1665  int success = 0;
1666  int db_locked = 0;
1667  mrchatlist_t* obj = mrchatlist_new(mailbox);
1668 
1669  mrsqlite3_lock(mailbox->m_sql);
1670  db_locked = 1;
1671 
1672  if( !mrchatlist_load_from_db__(obj, listflags, query) ) {
1673  goto cleanup;
1674  }
1675 
1676  /* success */
1677 
1678  success = 1;
1679 
1680  /* cleanup */
1681 cleanup:
1682  if( db_locked ) {
1683  mrsqlite3_unlock(mailbox->m_sql);
1684  }
1685 
1686  if( success ) {
1687  return obj;
1688  }
1689  else {
1690  mrchatlist_unref(obj);
1691  return NULL;
1692  }
1693 }
1694 
1695 
1709 mrchat_t* mrmailbox_get_chat(mrmailbox_t* mailbox, uint32_t chat_id)
1710 {
1711  int success = 0;
1712  int db_locked = 0;
1713  mrchat_t* obj = mrchat_new(mailbox);
1714 
1715  mrsqlite3_lock(mailbox->m_sql);
1716  db_locked = 1;
1717 
1718  if( !mrchat_load_from_db__(obj, chat_id) ) {
1719  goto cleanup;
1720  }
1721 
1722  /* success */
1723  success = 1;
1724 
1725  /* cleanup */
1726 cleanup:
1727  if( db_locked ) {
1728  mrsqlite3_unlock(mailbox->m_sql);
1729  }
1730 
1731  if( success ) {
1732  return obj;
1733  }
1734  else {
1735  mrchat_unref(obj);
1736  return NULL;
1737  }
1738 }
1739 
1740 
1755 void mrmailbox_marknoticed_chat(mrmailbox_t* mailbox, uint32_t chat_id)
1756 {
1757  /* marking a chat as "seen" is done by marking all fresh chat messages as "noticed" -
1758  "noticed" messages are not counted as being unread but are still waiting for being marked as "seen" using mrmailbox_markseen_msgs() */
1759  sqlite3_stmt* stmt;
1760 
1761  if( mailbox == NULL ) {
1762  return;
1763  }
1764 
1765  mrsqlite3_lock(mailbox->m_sql);
1766 
1767  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_state_WHERE_chat_id_AND_state,
1768  "UPDATE msgs SET state=" MR_STRINGIFY(MR_STATE_IN_NOTICED) " WHERE chat_id=? AND state=" MR_STRINGIFY(MR_STATE_IN_FRESH) ";");
1769  sqlite3_bind_int(stmt, 1, chat_id);
1770  sqlite3_step(stmt);
1771 
1772  mrsqlite3_unlock(mailbox->m_sql);
1773 }
1774 
1775 
1789 uint32_t mrmailbox_get_chat_id_by_contact_id(mrmailbox_t* mailbox, uint32_t contact_id)
1790 {
1791  uint32_t chat_id = 0;
1792 
1793  mrsqlite3_lock(mailbox->m_sql);
1794 
1795  chat_id = mrmailbox_lookup_real_nchat_by_contact_id__(mailbox, contact_id);
1796 
1797  mrsqlite3_unlock(mailbox->m_sql);
1798 
1799  return chat_id;
1800 }
1801 
1802 
1816 uint32_t mrmailbox_create_chat_by_contact_id(mrmailbox_t* mailbox, uint32_t contact_id)
1817 {
1818  uint32_t chat_id = 0;
1819  int send_event = 0, locked = 0;
1820 
1821  if( mailbox == NULL ) {
1822  return 0;
1823  }
1824 
1825  mrsqlite3_lock(mailbox->m_sql);
1826  locked = 1;
1827 
1828  chat_id = mrmailbox_lookup_real_nchat_by_contact_id__(mailbox, contact_id);
1829  if( chat_id ) {
1830  mrmailbox_log_warning(mailbox, 0, "Chat with contact %i already exists.", (int)contact_id);
1831  goto cleanup;
1832  }
1833 
1834  if( 0==mrmailbox_real_contact_exists__(mailbox, contact_id) ) {
1835  mrmailbox_log_warning(mailbox, 0, "Cannot create chat, contact %i does not exist.", (int)contact_id);
1836  goto cleanup;
1837  }
1838 
1839  chat_id = mrmailbox_create_or_lookup_nchat_by_contact_id__(mailbox, contact_id);
1840  if( chat_id ) {
1841  send_event = 1;
1842  }
1843 
1844  mrmailbox_scaleup_contact_origin__(mailbox, contact_id, MR_ORIGIN_CREATE_CHAT);
1845 
1846  mrsqlite3_unlock(mailbox->m_sql);
1847  locked = 0;
1848 
1849 cleanup:
1850  if( locked ) {
1851  mrsqlite3_unlock(mailbox->m_sql);
1852  }
1853 
1854  if( send_event ) {
1855  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, 0, 0);
1856  }
1857 
1858  return chat_id;
1859 }
1860 
1861 
1862 static carray* mrmailbox_get_chat_media__(mrmailbox_t* mailbox, uint32_t chat_id, int msg_type, int or_msg_type)
1863 {
1864  carray* ret = carray_new(100);
1865 
1866  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_i_FROM_msgs_WHERE_ctt,
1867  "SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=?) ORDER BY timestamp, id;");
1868  sqlite3_bind_int(stmt, 1, chat_id);
1869  sqlite3_bind_int(stmt, 2, msg_type);
1870  sqlite3_bind_int(stmt, 3, or_msg_type>0? or_msg_type : msg_type);
1871  while( sqlite3_step(stmt) == SQLITE_ROW ) {
1872  carray_add(ret, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
1873  }
1874 
1875  return ret;
1876 }
1877 
1878 
1896 carray* mrmailbox_get_chat_media(mrmailbox_t* mailbox, uint32_t chat_id, int msg_type, int or_msg_type)
1897 {
1898  carray* ret = NULL;
1899 
1900  if( mailbox ) {
1901  mrsqlite3_lock(mailbox->m_sql);
1902  ret = mrmailbox_get_chat_media__(mailbox, chat_id, msg_type, or_msg_type);
1903  mrsqlite3_unlock(mailbox->m_sql);
1904  }
1905 
1906  return ret;
1907 }
1908 
1909 
1927 uint32_t mrmailbox_get_next_media(mrmailbox_t* mailbox, uint32_t curr_msg_id, int dir)
1928 {
1929  uint32_t ret_msg_id = 0;
1930  mrmsg_t* msg = mrmsg_new();
1931  int locked = 0;
1932  carray* list = NULL;
1933  int i, cnt;
1934 
1935  if( mailbox == NULL ) {
1936  goto cleanup;
1937  }
1938 
1939  mrsqlite3_lock(mailbox->m_sql);
1940  locked = 1;
1941 
1942  if( !mrmsg_load_from_db__(msg, mailbox, curr_msg_id) ) {
1943  goto cleanup;
1944  }
1945 
1946  if( (list=mrmailbox_get_chat_media__(mailbox, msg->m_chat_id, msg->m_type, 0))==NULL ) {
1947  goto cleanup;
1948  }
1949 
1950  mrsqlite3_unlock(mailbox->m_sql);
1951  locked = 0;
1952 
1953  cnt = carray_count(list);
1954  for( i = 0; i < cnt; i++ ) {
1955  if( curr_msg_id == (uint32_t)(uintptr_t)carray_get(list, i) )
1956  {
1957  if( dir > 0 ) {
1958  /* get the next message from the current position */
1959  if( i+1 < cnt ) {
1960  ret_msg_id = (uint32_t)(uintptr_t)carray_get(list, i+1);
1961  }
1962  }
1963  else if( dir < 0 ) {
1964  /* get the previous message from the current position */
1965  if( i-1 >= 0 ) {
1966  ret_msg_id = (uint32_t)(uintptr_t)carray_get(list, i-1);
1967  }
1968  }
1969  break;
1970  }
1971  }
1972 
1973 
1974 cleanup:
1975  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
1976  if( list ) { carray_free(list); }
1977  mrmsg_unref(msg);
1978  return ret_msg_id;
1979 }
1980 
1981 
2003 carray* mrmailbox_get_chat_contacts(mrmailbox_t* mailbox, uint32_t chat_id)
2004 {
2005  /* Normal chats do not include SELF. Group chats do (as it may happen that one is deleted from a
2006  groupchat but the chats stays visible, moreover, this makes displaying lists easier) */
2007  carray* ret = carray_new(100);
2008  sqlite3_stmt* stmt;
2009 
2010  if( mailbox == NULL ) {
2011  goto cleanup;
2012  }
2013 
2014  mrsqlite3_lock(mailbox->m_sql);
2015 
2016  if( chat_id == MR_CHAT_ID_DEADDROP )
2017  {
2018  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_contacts_WHERE_chat_id,
2019  "SELECT DISTINCT from_id FROM msgs WHERE chat_id=? and from_id!=0 ORDER BY id DESC;"); /* from_id in the deaddrop chat may be 0, see comment [**] */
2020  sqlite3_bind_int(stmt, 1, chat_id);
2021  }
2022  else
2023  {
2024  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_c_FROM_chats_contacts_WHERE_c_ORDER_BY,
2025  "SELECT cc.contact_id FROM chats_contacts cc"
2026  " LEFT JOIN contacts c ON c.id=cc.contact_id"
2027  " WHERE cc.chat_id=?"
2028  " ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;");
2029  sqlite3_bind_int(stmt, 1, chat_id);
2030  }
2031 
2032  while( sqlite3_step(stmt) == SQLITE_ROW ) {
2033  carray_add(ret, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
2034  }
2035 
2036  mrsqlite3_unlock(mailbox->m_sql);
2037 
2038 cleanup:
2039  return ret;
2040 }
2041 
2042 
2043 
2053 {
2054  int show_deaddrop, success = 0, locked = 0;
2055  carray* ret = carray_new(128);
2056  sqlite3_stmt* stmt = NULL;
2057 
2058  if( mailbox==NULL || ret == NULL ) {
2059  goto cleanup;
2060  }
2061 
2062  mrsqlite3_lock(mailbox->m_sql);
2063  locked = 1;
2064 
2065  show_deaddrop = 0;//mrsqlite3_get_config_int__(mailbox->m_sql, "show_deaddrop", 0);
2066 
2067  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_fresh,
2068  "SELECT m.id"
2069  " FROM msgs m"
2070  " LEFT JOIN contacts ct ON m.from_id=ct.id"
2071  " WHERE m.state=" MR_STRINGIFY(MR_STATE_IN_FRESH) " AND m.chat_id!=? AND ct.blocked=0"
2072  " ORDER BY m.timestamp DESC,m.id DESC;"); /* the list starts with the newest messages*/
2073  sqlite3_bind_int(stmt, 1, show_deaddrop? 0 : MR_CHAT_ID_DEADDROP);
2074 
2075  while( sqlite3_step(stmt) == SQLITE_ROW ) {
2076  carray_add(ret, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
2077  }
2078 
2079  mrsqlite3_unlock(mailbox->m_sql);
2080  locked = 0;
2081 
2082  success = 1;
2083 
2084 cleanup:
2085  if( locked ) {
2086  mrsqlite3_unlock(mailbox->m_sql);
2087  }
2088 
2089  if( success ) {
2090  return ret;
2091  }
2092  else {
2093  if( ret ) {
2094  carray_free(ret);
2095  }
2096  return NULL;
2097  }
2098 }
2099 
2100 
2120 carray* mrmailbox_get_chat_msgs(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t flags, uint32_t marker1before)
2121 {
2122  int success = 0, locked = 0;
2123  carray* ret = carray_new(512);
2124  sqlite3_stmt* stmt = NULL;
2125 
2126  uint32_t curr_id;
2127  time_t curr_local_timestamp;
2128  int curr_day, last_day = 0;
2129  long cnv_to_local = mr_gm2local_offset();
2130  #define SECONDS_PER_DAY 86400
2131 
2132  if( mailbox==NULL || ret == NULL ) {
2133  goto cleanup;
2134  }
2135 
2136  mrsqlite3_lock(mailbox->m_sql);
2137  locked = 1;
2138 
2139  if( chat_id == MR_CHAT_ID_STARRED )
2140  {
2141  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_starred,
2142  "SELECT m.id, m.timestamp"
2143  " FROM msgs m"
2144  " LEFT JOIN contacts ct ON m.from_id=ct.id"
2145  " WHERE m.starred=1 AND ct.blocked=0"
2146  " ORDER BY m.timestamp,m.id;"); /* the list starts with the oldest message*/
2147  }
2148  else
2149  {
2150  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_i_FROM_msgs_LEFT_JOIN_contacts_WHERE_c,
2151  "SELECT m.id, m.timestamp"
2152  " FROM msgs m"
2153  " LEFT JOIN contacts ct ON m.from_id=ct.id"
2154  " WHERE m.chat_id=? AND ct.blocked=0"
2155  " ORDER BY m.timestamp,m.id;"); /* the list starts with the oldest message*/
2156  sqlite3_bind_int(stmt, 1, chat_id);
2157  }
2158 
2159  while( sqlite3_step(stmt) == SQLITE_ROW )
2160  {
2161  curr_id = sqlite3_column_int(stmt, 0);
2162 
2163  /* add user marker */
2164  if( curr_id == marker1before ) {
2165  carray_add(ret, (void*)MR_MSG_ID_MARKER1, NULL);
2166  }
2167 
2168  /* add daymarker, if needed */
2169  if( flags&MR_GCM_ADDDAYMARKER ) {
2170  curr_local_timestamp = (time_t)sqlite3_column_int64(stmt, 1) + cnv_to_local;
2171  curr_day = curr_local_timestamp/SECONDS_PER_DAY;
2172  if( curr_day != last_day ) {
2173  carray_add(ret, (void*)MR_MSG_ID_DAYMARKER, NULL);
2174  last_day = curr_day;
2175  }
2176  }
2177 
2178  carray_add(ret, (void*)(uintptr_t)curr_id, NULL);
2179  }
2180 
2181  mrsqlite3_unlock(mailbox->m_sql);
2182  locked = 0;
2183 
2184  success = 1;
2185 
2186 cleanup:
2187  if( locked ) {
2188  mrsqlite3_unlock(mailbox->m_sql);
2189  }
2190 
2191  if( success ) {
2192  return ret;
2193  }
2194  else {
2195  if( ret ) {
2196  carray_free(ret);
2197  }
2198  return NULL;
2199  }
2200 }
2201 
2202 
2224 carray* mrmailbox_search_msgs(mrmailbox_t* mailbox, uint32_t chat_id, const char* query)
2225 {
2226  int success = 0, locked = 0;
2227  carray* ret = carray_new(100);
2228  char* strLikeInText = NULL, *strLikeBeg=NULL, *real_query = NULL;
2229  sqlite3_stmt* stmt = NULL;
2230 
2231  if( mailbox==NULL || ret == NULL || query == NULL ) {
2232  goto cleanup;
2233  }
2234 
2235  real_query = safe_strdup(query);
2236  mr_trim(real_query);
2237  if( real_query[0]==0 ) {
2238  success = 1; /*empty result*/
2239  goto cleanup;
2240  }
2241 
2242  strLikeInText = mr_mprintf("%%%s%%", real_query);
2243  strLikeBeg = mr_mprintf("%s%%", real_query); /*for the name search, we use "Name%" which is fast as it can use the index ("%Name%" could not). */
2244 
2245  mrsqlite3_lock(mailbox->m_sql);
2246  locked = 1;
2247 
2248  /* Incremental search with "LIKE %query%" cannot take advantages from any index
2249  ("query%" could for COLLATE NOCASE indexes, see http://www.sqlite.org/optoverview.html#like_opt )
2250  An alternative may be the FULLTEXT sqlite stuff, however, this does not really help with incremental search.
2251  An extra table with all words and a COLLATE NOCASE indexes may help, however,
2252  this must be updated all the time and probably consumes more time than we can save in tenthousands of searches.
2253  For now, we just expect the following query to be fast enough :-) */
2254  #define QUR1 "SELECT m.id, m.timestamp" \
2255  " FROM msgs m" \
2256  " LEFT JOIN contacts ct ON m.from_id=ct.id" \
2257  " WHERE"
2258  #define QUR2 " AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?)"
2259  if( chat_id ) {
2260  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_i_FROM_msgs_WHERE_chat_id_AND_query,
2261  QUR1 " m.chat_id=? " QUR2 " ORDER BY m.timestamp,m.id;"); /* chats starts with the oldest message*/
2262  sqlite3_bind_int (stmt, 1, chat_id);
2263  sqlite3_bind_text(stmt, 2, strLikeInText, -1, SQLITE_STATIC);
2264  sqlite3_bind_text(stmt, 3, strLikeBeg, -1, SQLITE_STATIC);
2265  }
2266  else {
2267  int show_deaddrop = 0;//mrsqlite3_get_config_int__(mailbox->m_sql, "show_deaddrop", 0);
2268  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_i_FROM_msgs_WHERE_query,
2269  QUR1 " (m.chat_id>? OR m.chat_id=?) " QUR2 " ORDER BY m.timestamp DESC,m.id DESC;"); /* chat overview starts with the newest message*/
2270  sqlite3_bind_int (stmt, 1, MR_CHAT_ID_LAST_SPECIAL);
2271  sqlite3_bind_int (stmt, 2, show_deaddrop? MR_CHAT_ID_DEADDROP : MR_CHAT_ID_LAST_SPECIAL+1 /*just any ID that is already selected*/);
2272  sqlite3_bind_text(stmt, 3, strLikeInText, -1, SQLITE_STATIC);
2273  sqlite3_bind_text(stmt, 4, strLikeBeg, -1, SQLITE_STATIC);
2274  }
2275 
2276  while( sqlite3_step(stmt) == SQLITE_ROW ) {
2277  carray_add(ret, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
2278  }
2279 
2280  mrsqlite3_unlock(mailbox->m_sql);
2281  locked = 0;
2282 
2283  success = 1;
2284 
2285 cleanup:
2286  if( locked ) {
2287  mrsqlite3_unlock(mailbox->m_sql);
2288  }
2289  free(strLikeInText);
2290  free(strLikeBeg);
2291  free(real_query);
2292  if( success ) {
2293  return ret;
2294  }
2295  else {
2296  if( ret ) {
2297  carray_free(ret);
2298  }
2299  return NULL;
2300  }
2301 }
2302 
2303 
2304 static void set_draft_int(mrmailbox_t* mailbox, mrchat_t* chat, uint32_t chat_id, const char* msg)
2305 {
2306  sqlite3_stmt* stmt;
2307  mrchat_t* chat_to_delete = NULL;
2308 
2309  if( mailbox == NULL ) {
2310  goto cleanup;
2311  }
2312 
2313  if( chat==NULL ) {
2314  if( (chat=mrmailbox_get_chat(mailbox, chat_id)) == NULL ) {
2315  goto cleanup;
2316  }
2317  chat_to_delete = chat;
2318  }
2319 
2320  if( msg && msg[0]==0 ) {
2321  msg = NULL; /* an empty draft is no draft */
2322  }
2323 
2324  if( chat->m_draft_text==NULL && msg==NULL
2325  && chat->m_draft_timestamp==0 ) {
2326  goto cleanup; /* nothing to do - there is no old and no new draft */
2327  }
2328 
2329  if( chat->m_draft_timestamp && chat->m_draft_text && msg && strcmp(chat->m_draft_text, msg)==0 ) {
2330  goto cleanup; /* for equal texts, we do not update the timestamp */
2331  }
2332 
2333  /* save draft in object - NULL or empty: clear draft */
2334  free(chat->m_draft_text);
2335  chat->m_draft_text = msg? safe_strdup(msg) : NULL;
2336  chat->m_draft_timestamp = msg? time(NULL) : 0;
2337 
2338  /* save draft in database */
2339  mrsqlite3_lock(mailbox->m_sql);
2340 
2341  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_chats_SET_draft_WHERE_id,
2342  "UPDATE chats SET draft_timestamp=?, draft_txt=? WHERE id=?;");
2343  sqlite3_bind_int64(stmt, 1, chat->m_draft_timestamp);
2344  sqlite3_bind_text (stmt, 2, chat->m_draft_text? chat->m_draft_text : "", -1, SQLITE_STATIC); /* SQLITE_STATIC: we promise the buffer to be valid until the query is done */
2345  sqlite3_bind_int (stmt, 3, chat->m_id);
2346 
2347  sqlite3_step(stmt);
2348 
2349  mrsqlite3_unlock(mailbox->m_sql);
2350 
2351  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, 0, 0);
2352 
2353 cleanup:
2354  mrchat_unref(chat_to_delete);
2355 }
2356 
2357 
2371 void mrmailbox_set_draft(mrmailbox_t* mailbox, uint32_t chat_id, const char* msg)
2372 {
2373  set_draft_int(mailbox, NULL, chat_id, msg);
2374 }
2375 
2376 
2377 int mrchat_set_draft(mrchat_t* chat, const char* msg) /* deprecated */
2378 {
2379  set_draft_int(chat->m_mailbox, chat, chat->m_id, msg);
2380  return 1;
2381 }
2382 
2383 
2384 
2385 #define IS_SELF_IN_GROUP__ (mrmailbox_is_contact_in_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF)==1)
2386 #define DO_SEND_STATUS_MAILS (mrparam_get_int(chat->m_param, MRP_UNPROMOTED, 0)==0)
2387 
2388 
2389 int mrmailbox_get_fresh_msg_count__(mrmailbox_t* mailbox, uint32_t chat_id)
2390 {
2391  sqlite3_stmt* stmt = NULL;
2392 
2393  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_WHERE_state_AND_chat_id,
2394  "SELECT COUNT(*) FROM msgs WHERE state=" MR_STRINGIFY(MR_STATE_IN_FRESH) " AND chat_id=?;"); /* we have an index over the state-column, this should be sufficient as there are typically only few fresh messages */
2395  sqlite3_bind_int(stmt, 1, chat_id);
2396 
2397  if( sqlite3_step(stmt) != SQLITE_ROW ) {
2398  return 0;
2399  }
2400 
2401  return sqlite3_column_int(stmt, 0);
2402 }
2403 
2404 
2405 uint32_t mrmailbox_get_last_deaddrop_fresh_msg__(mrmailbox_t* mailbox)
2406 {
2407  sqlite3_stmt* stmt = NULL;
2408 
2409  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_fresh_AND_deaddrop,
2410  "SELECT id FROM msgs WHERE state=" MR_STRINGIFY(MR_STATE_IN_FRESH) " AND chat_id=" MR_STRINGIFY(MR_CHAT_ID_DEADDROP) " ORDER BY timestamp DESC, id DESC;"); /* we have an index over the state-column, this should be sufficient as there are typically only few fresh messages */
2411 
2412  if( sqlite3_step(stmt) != SQLITE_ROW ) {
2413  return 0;
2414  }
2415 
2416  return sqlite3_column_int(stmt, 0);
2417 }
2418 
2419 
2420 int mrmailbox_get_total_msg_count__(mrmailbox_t* mailbox, uint32_t chat_id)
2421 {
2422  sqlite3_stmt* stmt = NULL;
2423 
2424  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_WHERE_chat_id,
2425  "SELECT COUNT(*) FROM msgs WHERE chat_id=?;");
2426  sqlite3_bind_int(stmt, 1, chat_id);
2427 
2428  if( sqlite3_step(stmt) != SQLITE_ROW ) {
2429  return 0;
2430  }
2431 
2432  return sqlite3_column_int(stmt, 0);
2433 }
2434 
2435 
2436 size_t mrmailbox_get_chat_cnt__(mrmailbox_t* mailbox)
2437 {
2438  sqlite3_stmt* stmt;
2439 
2440  if( mailbox == NULL || mailbox->m_sql->m_cobj==NULL ) {
2441  return 0; /* no database, no chats - this is no error (needed eg. for information) */
2442  }
2443 
2444  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_chats, "SELECT COUNT(*) FROM chats WHERE id>?;");
2445  sqlite3_bind_int(stmt, 1, MR_CHAT_ID_LAST_SPECIAL);
2446 
2447  if( sqlite3_step(stmt) != SQLITE_ROW ) {
2448  return 0;
2449  }
2450 
2451  return sqlite3_column_int(stmt, 0);
2452 }
2453 
2454 
2455 uint32_t mrmailbox_lookup_real_nchat_by_contact_id__(mrmailbox_t* mailbox, uint32_t contact_id) /* checks for "real" chats (non-trash, non-unknown) */
2456 {
2457  sqlite3_stmt* stmt;
2458  uint32_t chat_id = 0;
2459 
2460  if( mailbox == NULL || mailbox->m_sql->m_cobj==NULL ) {
2461  return 0; /* no database, no chats - this is no error (needed eg. for information) */
2462  }
2463 
2464  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_chats_WHERE_contact_id,
2465  "SELECT c.id"
2466  " FROM chats c"
2467  " INNER JOIN chats_contacts j ON c.id=j.chat_id"
2468  " WHERE c.type=? AND c.id>? AND j.contact_id=?;");
2469  sqlite3_bind_int(stmt, 1, MR_CHAT_TYPE_NORMAL);
2470  sqlite3_bind_int(stmt, 2, MR_CHAT_ID_LAST_SPECIAL);
2471  sqlite3_bind_int(stmt, 3, contact_id);
2472 
2473  if( sqlite3_step(stmt) == SQLITE_ROW ) {
2474  chat_id = sqlite3_column_int(stmt, 0);
2475  }
2476 
2477  return chat_id;
2478 }
2479 
2480 
2481 uint32_t mrmailbox_create_or_lookup_nchat_by_contact_id__(mrmailbox_t* mailbox, uint32_t contact_id)
2482 {
2483  uint32_t chat_id = 0;
2484  mrcontact_t* contact = NULL;
2485  char* chat_name;
2486  char* q = NULL;
2487  sqlite3_stmt* stmt = NULL;
2488 
2489  if( mailbox == NULL || mailbox->m_sql->m_cobj==NULL ) {
2490  return 0; /* database not opened - error */
2491  }
2492 
2493  if( contact_id == 0 ) {
2494  return 0;
2495  }
2496 
2497  if( (chat_id=mrmailbox_lookup_real_nchat_by_contact_id__(mailbox, contact_id)) != 0 ) {
2498  return chat_id; /* soon success */
2499  }
2500 
2501  /* get fine chat name */
2502  contact = mrcontact_new(mailbox);
2503  if( !mrcontact_load_from_db__(contact, mailbox->m_sql, contact_id) ) {
2504  goto cleanup;
2505  }
2506 
2507  chat_name = (contact->m_name&&contact->m_name[0])? contact->m_name : contact->m_addr;
2508 
2509  /* create chat record */
2510  q = sqlite3_mprintf("INSERT INTO chats (type, name) VALUES(%i, %Q)", MR_CHAT_TYPE_NORMAL, chat_name);
2511  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, q);
2512  if( stmt == NULL) {
2513  goto cleanup;
2514  }
2515 
2516  if( sqlite3_step(stmt) != SQLITE_DONE ) {
2517  goto cleanup;
2518  }
2519 
2520  chat_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
2521 
2522  sqlite3_free(q);
2523  q = NULL;
2524  sqlite3_finalize(stmt);
2525  stmt = NULL;
2526 
2527  /* add contact IDs to the new chat record (may be replaced by mrmailbox_add_contact_to_chat__()) */
2528  q = sqlite3_mprintf("INSERT INTO chats_contacts (chat_id, contact_id) VALUES(%i, %i)", chat_id, contact_id);
2529  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, q);
2530 
2531  if( sqlite3_step(stmt) != SQLITE_DONE ) {
2532  goto cleanup;
2533  }
2534 
2535  sqlite3_free(q);
2536  q = NULL;
2537  sqlite3_finalize(stmt);
2538  stmt = NULL;
2539 
2540  /* add already existing messages to the chat record */
2541  q = sqlite3_mprintf("UPDATE msgs SET chat_id=%i WHERE (chat_id=%i AND from_id=%i) OR (chat_id=%i AND to_id=%i);",
2542  chat_id,
2543  MR_CHAT_ID_DEADDROP, contact_id,
2544  MR_CHAT_ID_TO_DEADDROP, contact_id);
2545  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, q);
2546 
2547  if( sqlite3_step(stmt) != SQLITE_DONE ) {
2548  goto cleanup;
2549  }
2550 
2551  /* cleanup */
2552 cleanup:
2553  if( q ) {
2554  sqlite3_free(q);
2555  }
2556 
2557  if( stmt ) {
2558  sqlite3_finalize(stmt);
2559  }
2560 
2561  if( contact ) {
2562  mrcontact_unref(contact);
2563  }
2564  return chat_id;
2565 }
2566 
2567 
2568 void mrmailbox_unarchive_chat__(mrmailbox_t* mailbox, uint32_t chat_id)
2569 {
2570  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_chats_SET_unarchived, "UPDATE chats SET archived=0 WHERE id=?");
2571  sqlite3_bind_int (stmt, 1, chat_id);
2572  sqlite3_step(stmt);
2573 }
2574 
2575 
2576 
2588 int mrmailbox_get_total_msg_count(mrmailbox_t* mailbox, uint32_t chat_id)
2589 {
2590  int ret;
2591 
2592  if( mailbox == NULL ) {
2593  return 0;
2594  }
2595 
2596  mrsqlite3_lock(mailbox->m_sql);
2597  ret = mrmailbox_get_total_msg_count__(mailbox, chat_id);
2598  mrsqlite3_unlock(mailbox->m_sql);
2599 
2600  return ret;
2601 }
2602 
2603 
2616 int mrmailbox_get_fresh_msg_count(mrmailbox_t* mailbox, uint32_t chat_id)
2617 {
2618  int ret;
2619 
2620  if( mailbox == NULL ) {
2621  return 0;
2622  }
2623 
2624  mrsqlite3_lock(mailbox->m_sql);
2625  ret = mrmailbox_get_fresh_msg_count__(mailbox, chat_id);
2626  mrsqlite3_unlock(mailbox->m_sql);
2627 
2628  return ret;
2629 }
2630 
2631 
2652 void mrmailbox_archive_chat(mrmailbox_t* mailbox, uint32_t chat_id, int archive)
2653 {
2654  if( mailbox == NULL || chat_id <= MR_CHAT_ID_LAST_SPECIAL || (archive!=0 && archive!=1) ) {
2655  return;
2656  }
2657 
2658  mrsqlite3_lock(mailbox->m_sql);
2659  sqlite3_stmt* stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "UPDATE chats SET archived=? WHERE id=?;");
2660  sqlite3_bind_int (stmt, 1, archive);
2661  sqlite3_bind_int (stmt, 2, chat_id);
2662  sqlite3_step(stmt);
2663  sqlite3_finalize(stmt);
2664  mrsqlite3_unlock(mailbox->m_sql);
2665 }
2666 
2667 
2668 /*******************************************************************************
2669  * Delete a chat
2670  ******************************************************************************/
2671 
2672 
2673 /* _If_ deleting a group chat would implies to leave the group, things get complicated
2674 as this would require to send a message before the chat is deleted physically.
2675 To make things even more complicated, there may be other chat messages waiting to be send.
2676 
2677 We used the following approach:
2678 1. If we do not need to send a message, we delete the chat directly
2679 2. If we need to send a message, we set chats.blocked=1 and add the parameter
2680  MRP_DEL_AFTER_SEND with a random value to both, the last message to be send and to the
2681  chat (we would use msg_id, however, we may not get this in time)
2682 3. When the messag with the MRP_DEL_AFTER_SEND-value of the chat was send to IMAP, we physically
2683  delete the chat.
2684 
2685 However, from 2017-11-02, we do not implicitly leave the group as this results in different behaviours to normal
2686 chat and _only_ leaving a group is also a valid usecase. */
2687 
2688 
2689 int mrmailbox_delete_chat_part2(mrmailbox_t* mailbox, uint32_t chat_id)
2690 {
2691  int success = 0, locked = 0, pending_transaction = 0;
2692  mrchat_t* obj = mrchat_new(mailbox);
2693  char* q3 = NULL;
2694 
2695  mrsqlite3_lock(mailbox->m_sql);
2696  locked = 1;
2697 
2698  if( !mrchat_load_from_db__(obj, chat_id) ) {
2699  goto cleanup;
2700  }
2701 
2702  mrsqlite3_begin_transaction__(mailbox->m_sql);
2703  pending_transaction = 1;
2704 
2705  q3 = sqlite3_mprintf("DELETE FROM msgs WHERE chat_id=%i;", chat_id);
2706  if( !mrsqlite3_execute__(mailbox->m_sql, q3) ) {
2707  goto cleanup;
2708  }
2709  sqlite3_free(q3);
2710  q3 = NULL;
2711 
2712  q3 = sqlite3_mprintf("DELETE FROM chats_contacts WHERE chat_id=%i;", chat_id);
2713  if( !mrsqlite3_execute__(mailbox->m_sql, q3) ) {
2714  goto cleanup;
2715  }
2716  sqlite3_free(q3);
2717  q3 = NULL;
2718 
2719  q3 = sqlite3_mprintf("DELETE FROM chats WHERE id=%i;", chat_id);
2720  if( !mrsqlite3_execute__(mailbox->m_sql, q3) ) {
2721  goto cleanup;
2722  }
2723  sqlite3_free(q3);
2724  q3 = NULL;
2725 
2726  mrsqlite3_commit__(mailbox->m_sql);
2727  pending_transaction = 0;
2728 
2729  mrsqlite3_unlock(mailbox->m_sql);
2730  locked = 0;
2731 
2732  success = 1;
2733 
2734 cleanup:
2735  if( pending_transaction ) { mrsqlite3_rollback__(mailbox->m_sql); }
2736  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
2737  mrchat_unref(obj);
2738  if( q3 ) { sqlite3_free(q3); }
2739  return success;
2740 }
2741 
2742 
2773 void mrmailbox_delete_chat(mrmailbox_t* mailbox, uint32_t chat_id)
2774 {
2775  mrchat_t* chat = mrmailbox_get_chat(mailbox, chat_id);
2776  mrcontact_t* contact = NULL;
2777  mrmsg_t* msg = mrmsg_new();
2778 
2779  if( mailbox == NULL || chat_id <= MR_CHAT_ID_LAST_SPECIAL || chat == NULL ) {
2780  goto cleanup;
2781  }
2782 
2783  #ifdef GROUP_DELETE_IMPLIES_LEAVING
2784  if( chat->m_type == MR_CHAT_TYPE_GROUP
2785  && mrmailbox_is_contact_in_chat(mailbox, chat_id, MR_CONTACT_ID_SELF)
2786  && DO_SEND_STATUS_MAILS )
2787  {
2788  /* _first_ mark chat to being delete and _then_ send the message to inform others that we've quit the group
2789  (the order is important - otherwise the message may be send asynchronous before we update the group. */
2790  int link_msg_to_chat_deletion = (int)time(NULL);
2791 
2792  mrparam_set_int(chat->m_param, MRP_DEL_AFTER_SEND, link_msg_to_chat_deletion);
2793  mrsqlite3_lock(mailbox->m_sql);
2794  sqlite3_stmt* stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "UPDATE chats SET blocked=1, param=? WHERE id=?;");
2795  sqlite3_bind_text (stmt, 1, chat->m_param->m_packed, -1, SQLITE_STATIC);
2796  sqlite3_bind_int (stmt, 2, chat_id);
2797  sqlite3_step(stmt);
2798  sqlite3_finalize(stmt);
2799  mrmailbox_set_group_explicitly_left__(mailbox, chat->m_grpid);
2800  mrsqlite3_unlock(mailbox->m_sql);
2801 
2802  contact = mrmailbox_get_contact(mailbox, MR_CONTACT_ID_SELF);
2803  msg->m_type = MR_MSG_TEXT;
2804  msg->m_text = mrstock_str(MR_STR_MSGGROUPLEFT);
2805  mrparam_set_int(msg->m_param, MRP_SYSTEM_CMD, MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP);
2806  mrparam_set (msg->m_param, MRP_SYSTEM_CMD_PARAM, contact->m_addr);
2807  mrparam_set_int(msg->m_param, MRP_DEL_AFTER_SEND, link_msg_to_chat_deletion);
2808  mrmailbox_send_msg(mailbox, chat->m_id, msg);
2809  }
2810  else
2811  #endif
2812  {
2813  /* directly delete the chat */
2814  mrmailbox_delete_chat_part2(mailbox, chat_id);
2815  }
2816 
2817  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, 0, 0);
2818 
2819 cleanup:
2820  mrchat_unref(chat);
2821  mrcontact_unref(contact);
2822  mrmsg_unref(msg);
2823 }
2824 
2825 
2826 
2827 /*******************************************************************************
2828  * Sending messages
2829  ******************************************************************************/
2830 
2831 
2832 void mrmailbox_send_msg_to_imap(mrmailbox_t* mailbox, mrjob_t* job)
2833 {
2834  mrmimefactory_t mimefactory;
2835  char* server_folder = NULL;
2836  uint32_t server_uid = 0;
2837 
2838  mrmimefactory_init(&mimefactory, mailbox);
2839 
2840  /* connect to IMAP-server */
2841  if( !mrimap_is_connected(mailbox->m_imap) ) {
2842  mrmailbox_connect_to_imap(mailbox, NULL);
2843  if( !mrimap_is_connected(mailbox->m_imap) ) {
2844  mrjob_try_again_later(job, MR_STANDARD_DELAY);
2845  goto cleanup;
2846  }
2847  }
2848 
2849  /* create message */
2850  if( mrmimefactory_load_msg(&mimefactory, job->m_foreign_id)==0
2851  || mimefactory.m_from_addr == NULL ) {
2852  goto cleanup; /* should not happen as we've send the message to the SMTP server before */
2853  }
2854 
2855  if( !mrmimefactory_render(&mimefactory, 1/*encrypt to self*/) ) {
2856  goto cleanup; /* should not happen as we've send the message to the SMTP server before */
2857  }
2858 
2859  if( !mrimap_append_msg(mailbox->m_imap, mimefactory.m_msg->m_timestamp, mimefactory.m_out->str, mimefactory.m_out->len, &server_folder, &server_uid) ) {
2860  mrjob_try_again_later(job, MR_STANDARD_DELAY);
2861  goto cleanup;
2862  }
2863  else {
2864  mrsqlite3_lock(mailbox->m_sql);
2865  mrmailbox_update_server_uid__(mailbox, mimefactory.m_msg->m_rfc724_mid, server_folder, server_uid);
2866  mrsqlite3_unlock(mailbox->m_sql);
2867  }
2868 
2869  /* check, if the chat shall be deleted pysically */
2870  #ifdef GROUP_DELETE_IMPLIES_LEAVING
2871  if( mrparam_get_int(mimefactory.m_chat->m_param, MRP_DEL_AFTER_SEND, 0)!=0
2872  && mrparam_get_int(mimefactory.m_chat->m_param, MRP_DEL_AFTER_SEND, 0)==mrparam_get_int(mimefactory.m_msg->m_param, MRP_DEL_AFTER_SEND, 0) ) {
2873  mrmailbox_delete_chat_part2(mailbox, mimefactory.m_chat->m_id);
2874  }
2875  #endif
2876 
2877 cleanup:
2878  mrmimefactory_empty(&mimefactory);
2879  free(server_folder);
2880 }
2881 
2882 
2883 static void mark_as_error(mrmailbox_t* mailbox, mrmsg_t* msg)
2884 {
2885  if( mailbox==NULL || msg==NULL ) {
2886  return;
2887  }
2888 
2889  mrsqlite3_lock(mailbox->m_sql);
2890  mrmailbox_update_msg_state__(mailbox, msg->m_id, MR_STATE_OUT_ERROR);
2891  mrsqlite3_unlock(mailbox->m_sql);
2892  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, msg->m_chat_id, 0);
2893 }
2894 
2895 
2896 void mrmailbox_send_msg_to_smtp(mrmailbox_t* mailbox, mrjob_t* job)
2897 {
2898  mrmimefactory_t mimefactory;
2899 
2900  mrmimefactory_init(&mimefactory, mailbox);
2901 
2902  /* connect to SMTP server, if not yet done */
2903  if( !mrsmtp_is_connected(mailbox->m_smtp) ) {
2904  mrloginparam_t* loginparam = mrloginparam_new();
2905  mrsqlite3_lock(mailbox->m_sql);
2906  mrloginparam_read__(loginparam, mailbox->m_sql, "configured_");
2907  mrsqlite3_unlock(mailbox->m_sql);
2908  int connected = mrsmtp_connect(mailbox->m_smtp, loginparam);
2909  mrloginparam_unref(loginparam);
2910  if( !connected ) {
2911  mrjob_try_again_later(job, MR_STANDARD_DELAY);
2912  goto cleanup;
2913  }
2914  }
2915 
2916  /* load message data */
2917  if( !mrmimefactory_load_msg(&mimefactory, job->m_foreign_id)
2918  || mimefactory.m_from_addr == NULL ) {
2919  mrmailbox_log_warning(mailbox, 0, "Cannot load data to send, maybe the message is deleted in between.");
2920  goto cleanup; /* no redo, no IMAP - there won't be more recipients next time (as the data does not exist, there is no need in calling mark_as_error()) */
2921  }
2922 
2923  /* check if the message is ready (normally, only video files may be delayed this way) */
2924  if( mimefactory.m_increation ) {
2925  mrmailbox_log_info(mailbox, 0, "File is in creation, retrying later.");
2926  mrjob_try_again_later(job, MR_INCREATION_POLL);
2927  goto cleanup;
2928  }
2929 
2930  /* send message - it's okay if there are not recipients, this is a group with only OURSELF; we only upload to IMAP in this case */
2931  if( clist_count(mimefactory.m_recipients_addr) > 0 ) {
2932  if( !mrmimefactory_render(&mimefactory, 0/*encrypt_to_self*/) ) {
2933  mark_as_error(mailbox, mimefactory.m_msg);
2934  mrmailbox_log_error(mailbox, 0, "Empty message."); /* should not happen */
2935  goto cleanup; /* no redo, no IMAP - there won't be more recipients next time. */
2936  }
2937 
2938  /* have we guaranteed encryption but cannot fullfill it for any reason? Do not send the message then.*/
2939  if( mrparam_get_int(mimefactory.m_msg->m_param, MRP_GUARANTEE_E2EE, 0) && !mimefactory.m_out_encrypted ) {
2940  mark_as_error(mailbox, mimefactory.m_msg);
2941  mrmailbox_log_error(mailbox, 0, "End-to-end-encryption unavailable unexpectedly.");
2942  goto cleanup; /* unrecoverable */
2943  }
2944 
2945  if( !mrsmtp_send_msg(mailbox->m_smtp, mimefactory.m_recipients_addr, mimefactory.m_out->str, mimefactory.m_out->len) ) {
2946  mrsmtp_disconnect(mailbox->m_smtp);
2947  mrjob_try_again_later(job, MR_AT_ONCE); /* MR_AT_ONCE is only the _initial_ delay, if the second try failes, the delay gets larger */
2948  goto cleanup;
2949  }
2950  }
2951 
2952  /* done */
2953  mrsqlite3_lock(mailbox->m_sql);
2954  mrsqlite3_begin_transaction__(mailbox->m_sql);
2955 
2956  /* debug print? */
2957  if( mrsqlite3_get_config_int__(mailbox->m_sql, "save_eml", 0) ) {
2958  char* emlname = mr_mprintf("%s/to-smtp-%i.eml", mailbox->m_blobdir, (int)mimefactory.m_msg->m_id);
2959  FILE* emlfileob = fopen(emlname, "w");
2960  if( emlfileob ) {
2961  fwrite(mimefactory.m_out->str, 1, mimefactory.m_out->len, emlfileob);
2962  fclose(emlfileob);
2963  }
2964  free(emlname);
2965  }
2966 
2967  mrmailbox_update_msg_state__(mailbox, mimefactory.m_msg->m_id, MR_STATE_OUT_DELIVERED);
2968  if( mimefactory.m_out_encrypted && mrparam_get_int(mimefactory.m_msg->m_param, MRP_GUARANTEE_E2EE, 0)==0 ) {
2969  mrparam_set_int(mimefactory.m_msg->m_param, MRP_GUARANTEE_E2EE, 1); /* can upgrade to E2EE - fine! */
2970  mrmsg_save_param_to_disk__(mimefactory.m_msg);
2971  }
2972 
2973  if( (mailbox->m_imap->m_server_flags&MR_NO_EXTRA_IMAP_UPLOAD)==0 ) {
2974  mrjob_add__(mailbox, MRJ_SEND_MSG_TO_IMAP, mimefactory.m_msg->m_id, NULL); /* send message to IMAP in another job */
2975  }
2976 
2977  mrsqlite3_commit__(mailbox->m_sql);
2978  mrsqlite3_unlock(mailbox->m_sql);
2979 
2980  mailbox->m_cb(mailbox, MR_EVENT_MSG_DELIVERED, mimefactory.m_msg->m_chat_id, mimefactory.m_msg->m_id);
2981 
2982 cleanup:
2983  mrmimefactory_empty(&mimefactory);
2984 }
2985 
2986 
2987 uint32_t mrmailbox_send_msg_i__(mrmailbox_t* mailbox, mrchat_t* chat, const mrmsg_t* msg, time_t timestamp)
2988 {
2989  char* rfc724_mid = NULL;
2990  sqlite3_stmt* stmt;
2991  uint32_t msg_id = 0, to_id = 0;
2992 
2993  if( chat->m_type==MR_CHAT_TYPE_GROUP && !mrmailbox_is_contact_in_chat__(mailbox, chat->m_id, MR_CONTACT_ID_SELF) ) {
2994  mrmailbox_log_error(mailbox, MR_ERR_SELF_NOT_IN_GROUP, NULL);
2995  goto cleanup;
2996  }
2997 
2998  {
2999  char* from = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL);
3000  if( from == NULL ) { goto cleanup; }
3001  rfc724_mid = mr_create_outgoing_rfc724_mid(chat->m_type==MR_CHAT_TYPE_GROUP? chat->m_grpid : NULL, from);
3002  free(from);
3003  }
3004 
3005  if( chat->m_type == MR_CHAT_TYPE_NORMAL )
3006  {
3007  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_c_FROM_chats_contacts_WHERE_c,
3008  "SELECT contact_id FROM chats_contacts WHERE chat_id=?;");
3009  sqlite3_bind_int(stmt, 1, chat->m_id);
3010  if( sqlite3_step(stmt) != SQLITE_ROW ) {
3011  goto cleanup;
3012  }
3013  to_id = sqlite3_column_int(stmt, 0);
3014  }
3015  else if( chat->m_type == MR_CHAT_TYPE_GROUP )
3016  {
3017  if( mrparam_get_int(chat->m_param, MRP_UNPROMOTED, 0)==1 ) {
3018  /* mark group as being no longer unpromoted */
3019  mrparam_set(chat->m_param, MRP_UNPROMOTED, NULL);
3020  mrchat_update_param__(chat);
3021  }
3022  }
3023 
3024  /* check if we can guarantee E2EE for this message. If we can, we won't send the message without E2EE later (because of a reset, changed settings etc. - messages may be delayed significally if there is no network present) */
3025  int can_guarantee_e2ee = 0;
3026  if( mailbox->m_e2ee_enabled ) {
3027  can_guarantee_e2ee = 1;
3028  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_p_FROM_chats_contacs_JOIN_contacts_peerstates_WHERE_cc,
3029  "SELECT ps.prefer_encrypted "
3030  " FROM chats_contacts cc "
3031  " LEFT JOIN contacts c ON cc.contact_id=c.id "
3032  " LEFT JOIN acpeerstates ps ON c.addr=ps.addr "
3033  " WHERE cc.chat_id=? AND cc.contact_id>?;");
3034  sqlite3_bind_int(stmt, 1, chat->m_id);
3035  sqlite3_bind_int(stmt, 2, MR_CONTACT_ID_LAST_SPECIAL);
3036  while( sqlite3_step(stmt) == SQLITE_ROW )
3037  {
3038  int prefer_encrypted = sqlite3_column_type(stmt, 0)==SQLITE_NULL? MRA_PE_NOPREFERENCE : sqlite3_column_int(stmt, 0);
3039  if( prefer_encrypted != MRA_PE_MUTUAL ) { /* when gossip becomes available, gossip keys should be used only in groups */
3040  can_guarantee_e2ee = 0;
3041  break;
3042  }
3043  }
3044  }
3045 
3046  if( can_guarantee_e2ee ) {
3047  mrparam_set_int(msg->m_param, MRP_GUARANTEE_E2EE, 1);
3048  }
3049  else {
3050  /* if we cannot guarantee E2EE, clear the flag (may be set if the message was loaded from the database, eg. for forwarding messages ) */
3051  mrparam_set(msg->m_param, MRP_GUARANTEE_E2EE, NULL);
3052  }
3053  mrparam_set(msg->m_param, MRP_ERRONEOUS_E2EE, NULL); /* reset eg. on forwarding */
3054 
3055  /* add message to the database */
3056  stmt = mrsqlite3_predefine__(mailbox->m_sql, INSERT_INTO_msgs_mcftttstpb,
3057  "INSERT INTO msgs (rfc724_mid,chat_id,from_id,to_id, timestamp,type,state, txt,param) VALUES (?,?,?,?, ?,?,?, ?,?);");
3058  sqlite3_bind_text (stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
3059  sqlite3_bind_int (stmt, 2, MR_CHAT_ID_MSGS_IN_CREATION);
3060  sqlite3_bind_int (stmt, 3, MR_CONTACT_ID_SELF);
3061  sqlite3_bind_int (stmt, 4, to_id);
3062  sqlite3_bind_int64(stmt, 5, timestamp);
3063  sqlite3_bind_int (stmt, 6, msg->m_type);
3064  sqlite3_bind_int (stmt, 7, MR_STATE_OUT_PENDING);
3065  sqlite3_bind_text (stmt, 8, msg->m_text? msg->m_text : "", -1, SQLITE_STATIC);
3066  sqlite3_bind_text (stmt, 9, msg->m_param->m_packed, -1, SQLITE_STATIC);
3067  if( sqlite3_step(stmt) != SQLITE_DONE ) {
3068  goto cleanup;
3069  }
3070 
3071  msg_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
3072 
3073  /* finalize message object on database, we set the chat ID late as we don't know it sooner */
3074  mrmailbox_update_msg_chat_id__(mailbox, msg_id, chat->m_id);
3075  mrjob_add__(mailbox, MRJ_SEND_MSG_TO_SMTP, msg_id, NULL); /* resuts on an asynchronous call to mrmailbox_send_msg_to_smtp() */
3076 
3077 cleanup:
3078  free(rfc724_mid);
3079  return msg_id;
3080 }
3081 
3082 
3101 uint32_t mrmailbox_send_text_msg(mrmailbox_t* mailbox, uint32_t chat_id, const char* text_to_send)
3102 {
3103  mrmsg_t* msg = mrmsg_new();
3104  uint32_t ret = 0;
3105 
3106  if( mailbox == NULL || chat_id <= MR_CHAT_ID_LAST_SPECIAL || text_to_send == NULL ) {
3107  goto cleanup;
3108  }
3109 
3110  msg->m_type = MR_MSG_TEXT;
3111  mrmsg_set_text(msg, text_to_send);
3112 
3113  ret = mrmailbox_send_msg(mailbox, chat_id, msg);
3114 
3115 cleanup:
3116  mrmsg_unref(msg);
3117  return ret;
3118 }
3119 
3120 
3142 uint32_t mrmailbox_send_msg(mrmailbox_t* mailbox, uint32_t chat_id, mrmsg_t* msg)
3143 {
3144  char* pathNfilename = NULL;
3145 
3146  if( mailbox == NULL || msg == NULL || chat_id <= MR_CHAT_ID_LAST_SPECIAL ) {
3147  return 0;
3148  }
3149 
3150  msg->m_id = 0;
3151  msg->m_mailbox = mailbox;
3152 
3153  if( msg->m_type == MR_MSG_TEXT )
3154  {
3155  ; /* the caller should check if the message text is empty */
3156  }
3157  else if( MR_MSG_NEEDS_ATTACHMENT(msg->m_type) )
3158  {
3159  pathNfilename = mrparam_get(msg->m_param, MRP_FILE, NULL);
3160  if( pathNfilename )
3161  {
3162  /* Got an attachment. Take care, the file may not be ready in this moment!
3163  This is useful eg. if a video should be sended and already shown as "being processed" in the chat.
3164  In this case, the user should create an `.increation`; when the file is deleted later on, the message is sended.
3165  (we do not use a state in the database as this would make eg. forwarding such messages much more complicated) */
3166 
3167  if( msg->m_type == MR_MSG_FILE || msg->m_type == MR_MSG_IMAGE )
3168  {
3169  /* Correct the type, take care not to correct already very special formats as GIF or VOICE.
3170  Typical conversions:
3171  - from FILE to AUDIO/VIDEO/IMAGE
3172  - from FILE/IMAGE to GIF */
3173  int better_type = 0;
3174  char* better_mime = NULL;
3175  mrmsg_guess_msgtype_from_suffix(pathNfilename, &better_type, &better_mime);
3176  if( better_type ) {
3177  msg->m_type = better_type;
3178  mrparam_set(msg->m_param, MRP_MIMETYPE, better_mime);
3179  }
3180  free(better_mime);
3181  }
3182 
3183  if( (msg->m_type == MR_MSG_IMAGE || msg->m_type == MR_MSG_GIF)
3184  && (mrparam_get_int(msg->m_param, MRP_WIDTH, 0)<=0 || mrparam_get_int(msg->m_param, MRP_HEIGHT, 0)<=0) ) {
3185  /* set width/height of images, if not yet done */
3186  unsigned char* buf = NULL; size_t buf_bytes; uint32_t w, h;
3187  if( mr_read_file(pathNfilename, (void**)&buf, &buf_bytes, msg->m_mailbox) ) {
3188  if( mr_get_filemeta(buf, buf_bytes, &w, &h) ) {
3189  mrparam_set_int(msg->m_param, MRP_WIDTH, w);
3190  mrparam_set_int(msg->m_param, MRP_HEIGHT, h);
3191  }
3192  }
3193  free(buf);
3194  }
3195 
3196  mrmailbox_log_info(mailbox, 0, "Attaching \"%s\" for message type #%i.", pathNfilename, (int)msg->m_type);
3197 
3198  if( msg->m_text ) { free(msg->m_text); }
3199  if( msg->m_type == MR_MSG_AUDIO ) {
3200  char* filename = mr_get_filename(pathNfilename);
3201  char* author = mrparam_get(msg->m_param, MRP_AUTHORNAME, "");
3202  char* title = mrparam_get(msg->m_param, MRP_TRACKNAME, "");
3203  msg->m_text = mr_mprintf("%s %s %s", filename, author, title); /* for outgoing messages, also add the mediainfo. For incoming messages, this is not needed as the filename is build from these information */
3204  free(filename);
3205  free(author);
3206  free(title);
3207  }
3208  else if( MR_MSG_MAKE_FILENAME_SEARCHABLE(msg->m_type) ) {
3209  msg->m_text = mr_get_filename(pathNfilename);
3210  }
3211  else if( MR_MSG_MAKE_SUFFIX_SEARCHABLE(msg->m_type) ) {
3212  msg->m_text = mr_get_filesuffix_lc(pathNfilename);
3213  }
3214  }
3215  else
3216  {
3217  mrmailbox_log_error(mailbox, 0, "Attachment missing for message of type #%i.", (int)msg->m_type); /* should not happen */
3218  goto cleanup;
3219  }
3220  }
3221  else
3222  {
3223  mrmailbox_log_error(mailbox, 0, "Cannot send messages of type #%i.", (int)msg->m_type); /* should not happen */
3224  goto cleanup;
3225  }
3226 
3227  mrsqlite3_lock(mailbox->m_sql);
3228  mrsqlite3_begin_transaction__(mailbox->m_sql);
3229 
3230  mrmailbox_unarchive_chat__(mailbox, chat_id);
3231 
3232  mailbox->m_smtp->m_log_connect_errors = 1;
3233 
3234  {
3235  mrchat_t* chat = mrchat_new(mailbox);
3236  if( mrchat_load_from_db__(chat, chat_id) ) {
3237  msg->m_id = mrmailbox_send_msg_i__(mailbox, chat, msg, mr_create_smeared_timestamp__());
3238  }
3239  mrchat_unref(chat);
3240  }
3241 
3242  mrsqlite3_commit__(mailbox->m_sql);
3243  mrsqlite3_unlock(mailbox->m_sql);
3244 
3245  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, chat_id, msg->m_id);
3246 
3247 cleanup:
3248  free(pathNfilename);
3249  return msg->m_id;
3250 }
3251 
3252 
3253 /*******************************************************************************
3254  * Handle Group Chats
3255  ******************************************************************************/
3256 
3257 
3258 int mrmailbox_group_explicitly_left__(mrmailbox_t* mailbox, const char* grpid)
3259 {
3260  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_FROM_leftgrps_WHERE_grpid, "SELECT id FROM leftgrps WHERE grpid=?;");
3261  sqlite3_bind_text (stmt, 1, grpid, -1, SQLITE_STATIC);
3262  return (sqlite3_step(stmt)==SQLITE_ROW);
3263 }
3264 
3265 
3266 void mrmailbox_set_group_explicitly_left__(mrmailbox_t* mailbox, const char* grpid)
3267 {
3268  if( !mrmailbox_group_explicitly_left__(mailbox, grpid) )
3269  {
3270  sqlite3_stmt* stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "INSERT INTO leftgrps (grpid) VALUES(?);");
3271  sqlite3_bind_text (stmt, 1, grpid, -1, SQLITE_STATIC);
3272  sqlite3_step(stmt);
3273  sqlite3_finalize(stmt);
3274  }
3275 }
3276 
3277 
3278 static int mrmailbox_real_group_exists__(mrmailbox_t* mailbox, uint32_t chat_id)
3279 {
3280  sqlite3_stmt* stmt;
3281  int ret = 0;
3282 
3283  if( mailbox == NULL || mailbox->m_sql->m_cobj==NULL
3284  || chat_id <= MR_CHAT_ID_LAST_SPECIAL ) {
3285  return 0;
3286  }
3287 
3288  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_chats_WHERE_id,
3289  "SELECT id FROM chats WHERE id=? AND type=?;");
3290  sqlite3_bind_int(stmt, 1, chat_id);
3291  sqlite3_bind_int(stmt, 2, MR_CHAT_TYPE_GROUP);
3292 
3293  if( sqlite3_step(stmt) == SQLITE_ROW ) {
3294  ret = 1;
3295  }
3296 
3297  return ret;
3298 }
3299 
3300 
3301 int mrmailbox_add_contact_to_chat__(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t contact_id)
3302 {
3303  /* add a contact to a chat; the function does not check the type or if any of the record exist or are already added to the chat! */
3304  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, INSERT_INTO_chats_contacts,
3305  "INSERT INTO chats_contacts (chat_id, contact_id) VALUES(?, ?)");
3306  sqlite3_bind_int(stmt, 1, chat_id);
3307  sqlite3_bind_int(stmt, 2, contact_id);
3308  return (sqlite3_step(stmt)==SQLITE_DONE)? 1 : 0;
3309 }
3310 
3311 
3333 uint32_t mrmailbox_create_group_chat(mrmailbox_t* mailbox, const char* chat_name)
3334 {
3335  uint32_t chat_id = 0;
3336  int locked = 0;
3337  char* draft_txt = NULL, *grpid = NULL;
3338  sqlite3_stmt* stmt = NULL;
3339 
3340  if( mailbox == NULL || chat_name==NULL || chat_name[0]==0 ) {
3341  return 0;
3342  }
3343 
3344  mrsqlite3_lock(mailbox->m_sql);
3345  locked = 1;
3346 
3347  draft_txt = mrstock_str_repl_string(MR_STR_NEWGROUPDRAFT, chat_name);
3348  grpid = mr_create_id();
3349 
3350  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql,
3351  "INSERT INTO chats (type, name, draft_timestamp, draft_txt, grpid, param) VALUES(?, ?, ?, ?, ?, 'U=1');" /*U=MRP_UNPROMOTED*/ );
3352  sqlite3_bind_int (stmt, 1, MR_CHAT_TYPE_GROUP);
3353  sqlite3_bind_text (stmt, 2, chat_name, -1, SQLITE_STATIC);
3354  sqlite3_bind_int64(stmt, 3, time(NULL));
3355  sqlite3_bind_text (stmt, 4, draft_txt, -1, SQLITE_STATIC);
3356  sqlite3_bind_text (stmt, 5, grpid, -1, SQLITE_STATIC);
3357  if( sqlite3_step(stmt)!=SQLITE_DONE ) {
3358  goto cleanup;
3359  }
3360 
3361  if( (chat_id=sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj)) == 0 ) {
3362  goto cleanup;
3363  }
3364 
3365  if( mrmailbox_add_contact_to_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF) ) {
3366  goto cleanup;
3367  }
3368 
3369 cleanup:
3370  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
3371  if( stmt) { sqlite3_finalize(stmt); }
3372  free(draft_txt);
3373  free(grpid);
3374 
3375  if( chat_id ) {
3376  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, 0, 0);
3377  }
3378 
3379  return chat_id;
3380 }
3381 
3382 
3399 int mrmailbox_set_chat_name(mrmailbox_t* mailbox, uint32_t chat_id, const char* new_name)
3400 {
3401  /* the function only sets the names of group chats; normal chats get their names from the contacts */
3402  int success = 0, locked = 0;
3403  mrchat_t* chat = mrchat_new(mailbox);
3404  mrmsg_t* msg = mrmsg_new();
3405  char* q3 = NULL;
3406 
3407  if( mailbox==NULL || new_name==NULL || new_name[0]==0 ) {
3408  goto cleanup;
3409  }
3410 
3411  mrsqlite3_lock(mailbox->m_sql);
3412  locked = 1;
3413 
3414  if( 0==mrmailbox_real_group_exists__(mailbox, chat_id)
3415  || 0==mrchat_load_from_db__(chat, chat_id) ) {
3416  goto cleanup;
3417  }
3418 
3419  if( strcmp(chat->m_name, new_name)==0 ) {
3420  success = 1;
3421  goto cleanup; /* name not modified */
3422  }
3423 
3424  if( !IS_SELF_IN_GROUP__ ) {
3425  mrmailbox_log_error(mailbox, MR_ERR_SELF_NOT_IN_GROUP, NULL);
3426  goto cleanup; /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */
3427  }
3428 
3429  q3 = sqlite3_mprintf("UPDATE chats SET name=%Q WHERE id=%i;", new_name, chat_id);
3430  if( !mrsqlite3_execute__(mailbox->m_sql, q3) ) {
3431  goto cleanup;
3432  }
3433 
3434  mrsqlite3_unlock(mailbox->m_sql);
3435  locked = 0;
3436 
3437  /* send a status mail to all group members, also needed for outself to allow multi-client */
3438  if( DO_SEND_STATUS_MAILS )
3439  {
3440  msg->m_type = MR_MSG_TEXT;
3441  msg->m_text = mrstock_str_repl_string2(MR_STR_MSGGRPNAME, chat->m_name, new_name);
3442  mrparam_set_int(msg->m_param, MRP_SYSTEM_CMD, MR_SYSTEM_GROUPNAME_CHANGED);
3443  msg->m_id = mrmailbox_send_msg(mailbox, chat->m_id, msg);
3444  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, chat_id, msg->m_id);
3445  }
3446  mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
3447 
3448  success = 1;
3449 
3450 cleanup:
3451  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
3452  if( q3 ) { sqlite3_free(q3); }
3453  mrchat_unref(chat);
3454  mrmsg_unref(msg);
3455  return success;
3456 }
3457 
3458 
3476 int mrmailbox_set_chat_image(mrmailbox_t* mailbox, uint32_t chat_id, const char* new_image /*NULL=remove image*/)
3477 {
3478  int success = 0, locked = 0;;
3479  mrchat_t* chat = mrchat_new(mailbox);
3480  mrmsg_t* msg = mrmsg_new();
3481 
3482  if( mailbox==NULL ) {
3483  goto cleanup;
3484  }
3485 
3486  mrsqlite3_lock(mailbox->m_sql);
3487  locked = 1;
3488 
3489  if( 0==mrmailbox_real_group_exists__(mailbox, chat_id)
3490  || 0==mrchat_load_from_db__(chat, chat_id) ) {
3491  goto cleanup;
3492  }
3493 
3494  if( !IS_SELF_IN_GROUP__ ) {
3495  mrmailbox_log_error(mailbox, MR_ERR_SELF_NOT_IN_GROUP, NULL);
3496  goto cleanup; /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */
3497  }
3498 
3499  mrparam_set(chat->m_param, MRP_PROFILE_IMAGE, new_image/*may be NULL*/);
3500  if( !mrchat_update_param__(chat) ) {
3501  goto cleanup;
3502  }
3503 
3504  mrsqlite3_unlock(mailbox->m_sql);
3505  locked = 0;
3506 
3507  /* send a status mail to all group members, also needed for outself to allow multi-client */
3508  if( DO_SEND_STATUS_MAILS )
3509  {
3510  mrparam_set_int(msg->m_param, MRP_SYSTEM_CMD, MR_SYSTEM_GROUPIMAGE_CHANGED);
3511  mrparam_set (msg->m_param, MRP_SYSTEM_CMD_PARAM, new_image);
3512  msg->m_type = MR_MSG_TEXT;
3513  msg->m_text = mrstock_str(new_image? MR_STR_MSGGRPIMGCHANGED : MR_STR_MSGGRPIMGDELETED);
3514  msg->m_id = mrmailbox_send_msg(mailbox, chat->m_id, msg);
3515  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, chat_id, msg->m_id);
3516  }
3517  mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
3518 
3519  success = 1;
3520 
3521 cleanup:
3522  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
3523  mrchat_unref(chat);
3524  mrmsg_unref(msg);
3525  return success;
3526 }
3527 
3528 
3529 int mrmailbox_get_chat_contact_count__(mrmailbox_t* mailbox, uint32_t chat_id)
3530 {
3531  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_chats_contacts_WHERE_chat_id,
3532  "SELECT COUNT(*) FROM chats_contacts WHERE chat_id=?;");
3533  sqlite3_bind_int(stmt, 1, chat_id);
3534  if( sqlite3_step(stmt) == SQLITE_ROW ) {
3535  return sqlite3_column_int(stmt, 0);
3536  }
3537  return 0;
3538 }
3539 
3540 
3541 int mrmailbox_is_contact_in_chat__(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t contact_id)
3542 {
3543  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_void_FROM_chats_contacts_WHERE_chat_id_AND_contact_id,
3544  "SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id=?;");
3545  sqlite3_bind_int(stmt, 1, chat_id);
3546  sqlite3_bind_int(stmt, 2, contact_id);
3547  return (sqlite3_step(stmt) == SQLITE_ROW)? 1 : 0;
3548 }
3549 
3550 
3565 int mrmailbox_is_contact_in_chat(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t contact_id)
3566 {
3567  /* this function works for group and for normal chats, however, it is more useful for group chats.
3568  MR_CONTACT_ID_SELF may be used to check, if the user itself is in a group chat (MR_CONTACT_ID_SELF is not added to normal chats) */
3569  int ret = 0;
3570  if( mailbox ) {
3571  mrsqlite3_lock(mailbox->m_sql);
3572  ret = mrmailbox_is_contact_in_chat__(mailbox, chat_id, contact_id);
3573  mrsqlite3_unlock(mailbox->m_sql);
3574  }
3575  return ret;
3576 }
3577 
3578 
3595 int mrmailbox_add_contact_to_chat(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t contact_id /*may be MR_CONTACT_ID_SELF*/)
3596 {
3597  int success = 0, locked = 0;
3598  mrcontact_t* contact = mrmailbox_get_contact(mailbox, contact_id); /* mrcontact_load_from_db__() does not load SELF fields */
3599  mrchat_t* chat = mrchat_new(mailbox);
3600  mrmsg_t* msg = mrmsg_new();
3601  char* self_addr = NULL;
3602 
3603  if( mailbox == NULL || contact == NULL ) {
3604  goto cleanup;
3605  }
3606 
3607  mrsqlite3_lock(mailbox->m_sql);
3608  locked = 1;
3609 
3610  if( 0==mrmailbox_real_group_exists__(mailbox, chat_id) /*this also makes sure, not contacts are added to special or normal chats*/
3611  || (0==mrmailbox_real_contact_exists__(mailbox, contact_id) && contact_id!=MR_CONTACT_ID_SELF)
3612  || 0==mrchat_load_from_db__(chat, chat_id) ) {
3613  goto cleanup;
3614  }
3615 
3616  if( !IS_SELF_IN_GROUP__ ) {
3617  mrmailbox_log_error(mailbox, MR_ERR_SELF_NOT_IN_GROUP, NULL);
3618  goto cleanup; /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */
3619  }
3620 
3621  self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", "");
3622  if( strcasecmp(contact->m_addr, self_addr)==0 ) {
3623  goto cleanup; /* ourself is added using MR_CONTACT_ID_SELF, do not add it explicitly. if SELF is not in the group, members cannot be added at all. */
3624  }
3625 
3626  if( 1==mrmailbox_is_contact_in_chat__(mailbox, chat_id, contact_id) ) {
3627  success = 1;
3628  goto cleanup;
3629  }
3630 
3631  if( 0==mrmailbox_add_contact_to_chat__(mailbox, chat_id, contact_id) ) {
3632  goto cleanup;
3633  }
3634 
3635  mrsqlite3_unlock(mailbox->m_sql);
3636  locked = 0;
3637 
3638  /* send a status mail to all group members */
3639  if( DO_SEND_STATUS_MAILS )
3640  {
3641  msg->m_type = MR_MSG_TEXT;
3642  msg->m_text = mrstock_str_repl_string(MR_STR_MSGADDMEMBER, (contact->m_authname&&contact->m_authname[0])? contact->m_authname : contact->m_addr);
3643  mrparam_set_int(msg->m_param, MRP_SYSTEM_CMD, MR_SYSTEM_MEMBER_ADDED_TO_GROUP);
3644  mrparam_set (msg->m_param, MRP_SYSTEM_CMD_PARAM, contact->m_addr);
3645  msg->m_id = mrmailbox_send_msg(mailbox, chat->m_id, msg);
3646  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, chat_id, msg->m_id);
3647  }
3648  mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
3649 
3650  success = 1;
3651 
3652 cleanup:
3653  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
3654  mrchat_unref(chat);
3655  mrcontact_unref(contact);
3656  mrmsg_unref(msg);
3657  free(self_addr);
3658  return success;
3659 }
3660 
3661 
3678 int mrmailbox_remove_contact_from_chat(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t contact_id /*may be MR_CONTACT_ID_SELF*/)
3679 {
3680  int success = 0, locked = 0;
3681  mrcontact_t* contact = mrmailbox_get_contact(mailbox, contact_id); /* mrcontact_load_from_db__() does not load SELF fields */
3682  mrchat_t* chat = mrchat_new(mailbox);
3683  mrmsg_t* msg = mrmsg_new();
3684  char* q3 = NULL;
3685 
3686  if( mailbox == NULL || (contact_id<=MR_CONTACT_ID_LAST_SPECIAL && contact_id!=MR_CONTACT_ID_SELF) ) {
3687  goto cleanup; /* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */
3688  } /* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */
3689 
3690  mrsqlite3_lock(mailbox->m_sql);
3691  locked = 1;
3692 
3693  if( 0==mrmailbox_real_group_exists__(mailbox, chat_id)
3694  || 0==mrchat_load_from_db__(chat, chat_id) ) {
3695  goto cleanup;
3696  }
3697 
3698  if( !IS_SELF_IN_GROUP__ ) {
3699  mrmailbox_log_error(mailbox, MR_ERR_SELF_NOT_IN_GROUP, NULL);
3700  goto cleanup; /* we shoud respect this - whatever we send to the group, it gets discarded anyway! */
3701  }
3702 
3703  mrsqlite3_unlock(mailbox->m_sql);
3704  locked = 0;
3705 
3706  /* send a status mail to all group members - we need to do this before we update the database -
3707  otherwise the !IS_SELF_IN_GROUP__-check in mrchat_send_msg() will fail. */
3708  if( contact )
3709  {
3710  if( DO_SEND_STATUS_MAILS )
3711  {
3712  msg->m_type = MR_MSG_TEXT;
3713  if( contact->m_id == MR_CONTACT_ID_SELF ) {
3714  mrmailbox_set_group_explicitly_left__(mailbox, chat->m_grpid);
3715  msg->m_text = mrstock_str(MR_STR_MSGGROUPLEFT);
3716  }
3717  else {
3718  msg->m_text = mrstock_str_repl_string(MR_STR_MSGDELMEMBER, (contact->m_authname&&contact->m_authname[0])? contact->m_authname : contact->m_addr);
3719  }
3720  mrparam_set_int(msg->m_param, MRP_SYSTEM_CMD, MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP);
3721  mrparam_set (msg->m_param, MRP_SYSTEM_CMD_PARAM, contact->m_addr);
3722  msg->m_id = mrmailbox_send_msg(mailbox, chat->m_id, msg);
3723  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, chat_id, msg->m_id);
3724  }
3725  }
3726 
3727  mrsqlite3_lock(mailbox->m_sql);
3728  locked = 1;
3729 
3730  q3 = sqlite3_mprintf("DELETE FROM chats_contacts WHERE chat_id=%i AND contact_id=%i;", chat_id, contact_id);
3731  if( !mrsqlite3_execute__(mailbox->m_sql, q3) ) {
3732  goto cleanup;
3733  }
3734 
3735  mrsqlite3_unlock(mailbox->m_sql);
3736  locked = 0;
3737 
3738  mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
3739 
3740  success = 1;
3741 
3742 cleanup:
3743  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
3744  if( q3 ) { sqlite3_free(q3); }
3745  mrchat_unref(chat);
3746  mrcontact_unref(contact);
3747  mrmsg_unref(msg);
3748  return success;
3749 }
3750 
3751 
3752 
3753 /*******************************************************************************
3754  * Handle Contacts
3755  ******************************************************************************/
3756 
3757 
3758 int mrmailbox_real_contact_exists__(mrmailbox_t* mailbox, uint32_t contact_id)
3759 {
3760  sqlite3_stmt* stmt;
3761  int ret = 0;
3762 
3763  if( mailbox == NULL || mailbox->m_sql->m_cobj==NULL
3764  || contact_id <= MR_CONTACT_ID_LAST_SPECIAL ) {
3765  return 0;
3766  }
3767 
3768  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_contacts_WHERE_id,
3769  "SELECT id FROM contacts WHERE id=?;");
3770  sqlite3_bind_int(stmt, 1, contact_id);
3771 
3772  if( sqlite3_step(stmt) == SQLITE_ROW ) {
3773  ret = 1;
3774  }
3775 
3776  return ret;
3777 }
3778 
3779 
3780 size_t mrmailbox_get_real_contact_cnt__(mrmailbox_t* mailbox)
3781 {
3782  sqlite3_stmt* stmt;
3783 
3784  if( mailbox == NULL || mailbox->m_sql->m_cobj==NULL ) {
3785  return 0;
3786  }
3787 
3788  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_contacts, "SELECT COUNT(*) FROM contacts WHERE id>?;");
3789  sqlite3_bind_int(stmt, 1, MR_CONTACT_ID_LAST_SPECIAL);
3790  if( sqlite3_step(stmt) != SQLITE_ROW ) {
3791  return 0;
3792  }
3793 
3794  return sqlite3_column_int(stmt, 0);
3795 }
3796 
3797 
3798 uint32_t mrmailbox_add_or_lookup_contact__( mrmailbox_t* mailbox,
3799  const char* name /*can be NULL, the caller may use mr_normalize_name() before*/,
3800  const char* addr__,
3801  int origin,
3802  int* sth_modified )
3803 {
3804  sqlite3_stmt* stmt;
3805  uint32_t row_id = 0;
3806  int dummy;
3807  char* addr = NULL;
3808 
3809  if( sth_modified == NULL ) {
3810  sth_modified = &dummy;
3811  }
3812 
3813  *sth_modified = 0;
3814 
3815  if( mailbox == NULL || addr__ == NULL || origin <= 0 ) {
3816  return 0;
3817  }
3818 
3819  /* normalize the email-address:
3820  - remove leading `mailto:` */
3821  addr = mr_normalize_addr(addr__);
3822 
3823  /* rough check if email-address is valid */
3824  if( strlen(addr) < 3 || strchr(addr, '@')==NULL || strchr(addr, '.')==NULL ) {
3825  mrmailbox_log_warning(mailbox, 0, "Bad address \"%s\" for contact \"%s\".", addr, name?name:"<unset>");
3826  goto cleanup;
3827  }
3828 
3829  /* insert email-address to database or modify the record with the given email-address.
3830  we treat all email-addresses case-insensitive. */
3831  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_inao_FROM_contacts_a,
3832  "SELECT id, name, addr, origin, authname FROM contacts WHERE addr=? COLLATE NOCASE;");
3833  sqlite3_bind_text(stmt, 1, (const char*)addr, -1, SQLITE_STATIC);
3834  if( sqlite3_step(stmt) == SQLITE_ROW )
3835  {
3836  const char *row_name, *row_addr, *row_authname;
3837  int row_origin, update_addr = 0, update_name = 0, update_authname = 0;
3838 
3839  row_id = sqlite3_column_int(stmt, 0);
3840  row_name = (const char*)sqlite3_column_text(stmt, 1); if( row_name == NULL ) { row_name = ""; }
3841  row_addr = (const char*)sqlite3_column_text(stmt, 2); if( row_addr == NULL ) { row_addr = addr; }
3842  row_origin = sqlite3_column_int(stmt, 3);
3843  row_authname = (const char*)sqlite3_column_text(stmt, 4); if( row_authname == NULL ) { row_authname = ""; }
3844 
3845  if( name && name[0] ) {
3846  if( row_name && row_name[0] ) {
3847  if( origin>=row_origin && strcmp(name, row_name)!=0 ) {
3848  update_name = 1;
3849  }
3850  }
3851  else {
3852  update_name = 1;
3853  }
3854 
3855  if( origin == MR_ORIGIN_INCOMING_UNKNOWN_FROM && strcmp(name, row_authname)!=0 ) {
3856  update_authname = 1;
3857  }
3858  }
3859 
3860  if( origin>=row_origin && strcmp(addr, row_addr)!=0 /*really compare case-sensitive here*/ ) {
3861  update_addr = 1;
3862  }
3863 
3864  if( update_name || update_authname || update_addr || origin>row_origin )
3865  {
3866  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_contacts_nao_WHERE_i,
3867  "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;");
3868  sqlite3_bind_text(stmt, 1, update_name? name : row_name, -1, SQLITE_STATIC);
3869  sqlite3_bind_text(stmt, 2, update_addr? addr : row_addr, -1, SQLITE_STATIC);
3870  sqlite3_bind_int (stmt, 3, origin>row_origin? origin : row_origin);
3871  sqlite3_bind_text(stmt, 4, update_authname? name : row_authname, -1, SQLITE_STATIC);
3872  sqlite3_bind_int (stmt, 5, row_id);
3873  sqlite3_step (stmt);
3874 
3875  if( update_name )
3876  {
3877  /* Update the contact name also if it is used as a group name.
3878  This is one of the few duplicated data, however, getting the chat list is much faster this way.*/
3879  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_chats_SET_n_WHERE_c,
3880  "UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);");
3881  sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
3882  sqlite3_bind_int (stmt, 2, MR_CHAT_TYPE_NORMAL);
3883  sqlite3_bind_int (stmt, 3, row_id);
3884  sqlite3_step (stmt);
3885  }
3886  }
3887 
3888  *sth_modified = 1;
3889  }
3890  else
3891  {
3892  stmt = mrsqlite3_predefine__(mailbox->m_sql, INSERT_INTO_contacts_neo,
3893  "INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);");
3894  sqlite3_bind_text(stmt, 1, name? name : "", -1, SQLITE_STATIC); /* avoid NULL-fields in column */
3895  sqlite3_bind_text(stmt, 2, addr, -1, SQLITE_STATIC);
3896  sqlite3_bind_int (stmt, 3, origin);
3897  if( sqlite3_step(stmt) == SQLITE_DONE )
3898  {
3899  row_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
3900  *sth_modified = 1;
3901  }
3902  else
3903  {
3904  mrmailbox_log_error(mailbox, 0, "Cannot add contact."); /* should not happen */
3905  }
3906  }
3907 
3908 cleanup:
3909  free(addr);
3910  return row_id;
3911 }
3912 
3913 
3914 void mrmailbox_scaleup_contact_origin__(mrmailbox_t* mailbox, uint32_t contact_id, int origin)
3915 {
3916  if( mailbox == NULL ) {
3917  return;
3918  }
3919 
3920  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_contacts_SET_origin_WHERE_id,
3921  "UPDATE contacts SET origin=? WHERE id=? AND origin<?;");
3922  sqlite3_bind_int(stmt, 1, origin);
3923  sqlite3_bind_int(stmt, 2, contact_id);
3924  sqlite3_bind_int(stmt, 3, origin);
3925  sqlite3_step(stmt);
3926 }
3927 
3928 
3929 int mrmailbox_is_contact_blocked__(mrmailbox_t* mailbox, uint32_t contact_id)
3930 {
3931  int is_blocked = 0;
3932  mrcontact_t* ths = mrcontact_new();
3933 
3934  if( mrcontact_load_from_db__(ths, mailbox->m_sql, contact_id) ) { /* we could optimize this by loading only the needed fields */
3935  if( ths->m_blocked ) {
3936  is_blocked = 1;
3937  }
3938  }
3939 
3940  mrcontact_unref(ths);
3941  return is_blocked;
3942 }
3943 
3944 
3945 int mrmailbox_get_contact_origin__(mrmailbox_t* mailbox, uint32_t contact_id, int* ret_blocked)
3946 {
3947  int ret = MR_ORIGIN_UNSET;
3948  int dummy; if( ret_blocked==NULL ) { ret_blocked = &dummy; }
3949  mrcontact_t* ths = mrcontact_new();
3950 
3951  *ret_blocked = 0;
3952 
3953  if( !mrcontact_load_from_db__(ths, mailbox->m_sql, contact_id) ) { /* we could optimize this by loading only the needed fields */
3954  goto cleanup;
3955  }
3956 
3957  if( ths->m_blocked ) {
3958  *ret_blocked = 1;
3959  goto cleanup;
3960  }
3961 
3962  ret = ths->m_origin;
3963 
3964 cleanup:
3965  mrcontact_unref(ths);
3966  return ret;
3967 }
3968 
3969 
3985 uint32_t mrmailbox_create_contact(mrmailbox_t* mailbox, const char* name, const char* addr)
3986 {
3987  uint32_t contact_id = 0;
3988 
3989  if( mailbox == NULL || addr == NULL || addr[0]==0 ) {
3990  goto cleanup;
3991  }
3992 
3993  mrsqlite3_lock(mailbox->m_sql);
3994 
3995  contact_id = mrmailbox_add_or_lookup_contact__(mailbox, name, addr, MR_ORIGIN_MANUALLY_CREATED, NULL);
3996 
3997  mrsqlite3_unlock(mailbox->m_sql);
3998 
3999  mailbox->m_cb(mailbox, MR_EVENT_CONTACTS_CHANGED, 0, 0);
4000 
4001 cleanup:
4002  return contact_id;
4003 }
4004 
4005 
4021 int mrmailbox_add_address_book(mrmailbox_t* mailbox, const char* adr_book) /* format: Name one\nAddress one\nName two\Address two */
4022 {
4023  carray* lines = NULL;
4024  size_t i, iCnt;
4025  int sth_modified, modify_cnt = 0;
4026 
4027  if( mailbox == NULL || adr_book == NULL ) {
4028  goto cleanup;
4029  }
4030 
4031  if( (lines=mr_split_into_lines(adr_book))==NULL ) {
4032  goto cleanup;
4033  }
4034 
4035  mrsqlite3_lock(mailbox->m_sql);
4036 
4037  mrsqlite3_begin_transaction__(mailbox->m_sql);
4038 
4039  iCnt = carray_count(lines);
4040  for( i = 0; i+1 < iCnt; i += 2 ) {
4041  char* name = (char*)carray_get(lines, i);
4042  char* addr = (char*)carray_get(lines, i+1);
4044  mrmailbox_add_or_lookup_contact__(mailbox, name, addr, MR_ORIGIN_ADRESS_BOOK, &sth_modified);
4045  if( sth_modified ) {
4046  modify_cnt++;
4047  }
4048  }
4049 
4050  mrsqlite3_commit__(mailbox->m_sql);
4051 
4052  mrsqlite3_unlock(mailbox->m_sql);
4053 
4054 cleanup:
4055  mr_free_splitted_lines(lines);
4056 
4057  return modify_cnt;
4058 }
4059 
4060 
4076 carray* mrmailbox_get_known_contacts(mrmailbox_t* mailbox, const char* query)
4077 {
4078  int locked = 0;
4079  carray* ret = carray_new(100);
4080  char* s3strLikeCmd = NULL;
4081  sqlite3_stmt* stmt;
4082 
4083  if( mailbox == NULL ) {
4084  goto cleanup;
4085  }
4086 
4087  mrsqlite3_lock(mailbox->m_sql);
4088  locked = 1;
4089 
4090  if( query ) {
4091  if( (s3strLikeCmd=sqlite3_mprintf("%%%s%%", query))==NULL ) {
4092  goto cleanup;
4093  }
4094  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_contacts_WHERE_query_ORDER_BY,
4095  "SELECT id FROM contacts"
4096  " WHERE id>? AND origin>=? AND blocked=0 AND (name LIKE ? OR addr LIKE ?)" /* see comments in mrmailbox_search_msgs() about the LIKE operator */
4097  " ORDER BY LOWER(name||addr),id;");
4098  sqlite3_bind_int (stmt, 1, MR_CONTACT_ID_LAST_SPECIAL);
4099  sqlite3_bind_int (stmt, 2, MR_ORIGIN_MIN_CONTACT_LIST);
4100  sqlite3_bind_text(stmt, 3, s3strLikeCmd, -1, SQLITE_STATIC);
4101  sqlite3_bind_text(stmt, 4, s3strLikeCmd, -1, SQLITE_STATIC);
4102  }
4103  else {
4104  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_contacts_ORDER_BY,
4105  "SELECT id FROM contacts"
4106  " WHERE id>? AND origin>=? AND blocked=0"
4107  " ORDER BY LOWER(name||addr),id;");
4108  sqlite3_bind_int(stmt, 1, MR_CONTACT_ID_LAST_SPECIAL);
4109  sqlite3_bind_int(stmt, 2, MR_ORIGIN_MIN_CONTACT_LIST);
4110  }
4111 
4112  while( sqlite3_step(stmt) == SQLITE_ROW ) {
4113  carray_add(ret, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
4114  }
4115 
4116  mrsqlite3_unlock(mailbox->m_sql);
4117  locked = 0;
4118 
4119 cleanup:
4120  if( locked ) {
4121  mrsqlite3_unlock(mailbox->m_sql);
4122  }
4123  if( s3strLikeCmd ) {
4124  sqlite3_free(s3strLikeCmd);
4125  }
4126  return ret;
4127 }
4128 
4129 
4141 {
4142  carray* ret = carray_new(100);
4143  sqlite3_stmt* stmt;
4144 
4145  if( mailbox == NULL ) {
4146  goto cleanup;
4147  }
4148 
4149  mrsqlite3_lock(mailbox->m_sql);
4150 
4151  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_contacts_WHERE_blocked,
4152  "SELECT id FROM contacts"
4153  " WHERE id>? AND blocked!=0"
4154  " ORDER BY LOWER(name||addr),id;");
4155  sqlite3_bind_int(stmt, 1, MR_CONTACT_ID_LAST_SPECIAL);
4156  while( sqlite3_step(stmt) == SQLITE_ROW ) {
4157  carray_add(ret, (void*)(uintptr_t)sqlite3_column_int(stmt, 0), NULL);
4158  }
4159 
4160  mrsqlite3_unlock(mailbox->m_sql);
4161 
4162 cleanup:
4163  return ret;
4164 }
4165 
4166 
4175 {
4176  int ret = 0, locked = 0;
4177  sqlite3_stmt* stmt;
4178 
4179  if( mailbox == NULL ) {
4180  goto cleanup;
4181  }
4182 
4183  mrsqlite3_lock(mailbox->m_sql);
4184  locked = 1;
4185 
4186  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_contacts_WHERE_blocked,
4187  "SELECT COUNT(*) FROM contacts"
4188  " WHERE id>? AND blocked!=0");
4189  sqlite3_bind_int(stmt, 1, MR_CONTACT_ID_LAST_SPECIAL);
4190  if( sqlite3_step(stmt) != SQLITE_ROW ) {
4191  goto cleanup;
4192  }
4193  ret = sqlite3_column_int(stmt, 0);
4194 
4195  mrsqlite3_unlock(mailbox->m_sql);
4196  locked = 0;
4197 
4198 cleanup:
4199  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
4200  return ret;
4201 }
4202 
4203 
4216 mrcontact_t* mrmailbox_get_contact(mrmailbox_t* mailbox, uint32_t contact_id)
4217 {
4218  mrcontact_t* ret = mrcontact_new();
4219 
4220  mrsqlite3_lock(mailbox->m_sql);
4221 
4222  if( contact_id == MR_CONTACT_ID_SELF )
4223  {
4224  ret->m_id = contact_id;
4225  ret->m_name = mrstock_str(MR_STR_SELF);
4226  ret->m_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL);
4227  }
4228  else
4229  {
4230  if( !mrcontact_load_from_db__(ret, mailbox->m_sql, contact_id) ) {
4231  mrcontact_unref(ret);
4232  ret = NULL;
4233  }
4234  }
4235 
4236  mrsqlite3_unlock(mailbox->m_sql);
4237 
4238  return ret; /* may be NULL */
4239 }
4240 
4241 
4242 static void marknoticed_contact__(mrmailbox_t* mailbox, uint32_t contact_id)
4243 {
4244  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_state_WHERE_from_id_AND_state,
4245  "UPDATE msgs SET state=" MR_STRINGIFY(MR_STATE_IN_NOTICED) " WHERE from_id=? AND state=" MR_STRINGIFY(MR_STATE_IN_FRESH) ";");
4246  sqlite3_bind_int(stmt, 1, contact_id);
4247  sqlite3_step(stmt);
4248 }
4249 
4250 
4264 void mrmailbox_marknoticed_contact(mrmailbox_t* mailbox, uint32_t contact_id)
4265 {
4266  if( mailbox == NULL ) {
4267  return;
4268  }
4269  mrsqlite3_lock(mailbox->m_sql);
4270  marknoticed_contact__(mailbox, contact_id);
4271  mrsqlite3_unlock(mailbox->m_sql);
4272 }
4273 
4274 
4290 void mrmailbox_block_contact(mrmailbox_t* mailbox, uint32_t contact_id, int new_blocking)
4291 {
4292  int locked = 0, send_event = 0, transaction_pending = 0;
4293  mrcontact_t* contact = mrcontact_new();
4294  sqlite3_stmt* stmt;
4295 
4296  if( mailbox == NULL ) {
4297  return;
4298  }
4299 
4300  mrsqlite3_lock(mailbox->m_sql);
4301  locked = 1;
4302 
4303  if( mrcontact_load_from_db__(contact, mailbox->m_sql, contact_id)
4304  && contact->m_blocked != new_blocking )
4305  {
4306  mrsqlite3_begin_transaction__(mailbox->m_sql);
4307  transaction_pending = 1;
4308 
4309  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_contacts_SET_b_WHERE_i,
4310  "UPDATE contacts SET blocked=? WHERE id=?;");
4311  sqlite3_bind_int(stmt, 1, new_blocking);
4312  sqlite3_bind_int(stmt, 2, contact_id);
4313  if( sqlite3_step(stmt)!=SQLITE_DONE ) {
4314  goto cleanup;
4315  }
4316 
4317  /* also (un)block all chats with _only_ this contact - we do not delete them to allow a non-destructive blocking->unblocking.
4318  (Maybe, beside normal chats (type=100) we should also block group chats with only this user.
4319  However, I'm not sure about this point; it may be confusing if the user wants to add other people;
4320  this would result in recreating the same group...) */
4321  stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_chats_SET_blocked,
4322  "UPDATE chats SET blocked=? WHERE type=? AND id IN (SELECT chat_id FROM chats_contacts WHERE contact_id=?);");
4323  sqlite3_bind_int(stmt, 1, new_blocking);
4324  sqlite3_bind_int(stmt, 2, MR_CHAT_TYPE_NORMAL);
4325  sqlite3_bind_int(stmt, 3, contact_id);
4326  if( sqlite3_step(stmt)!=SQLITE_DONE ) {
4327  goto cleanup;
4328  }
4329 
4330  /* mark all messages from the blocked contact as being noticed (this is to remove the deaddrop popup) */
4331  marknoticed_contact__(mailbox, contact_id);
4332 
4333  mrsqlite3_commit__(mailbox->m_sql);
4334  transaction_pending = 0;
4335 
4336  send_event = 1;
4337  }
4338 
4339  mrsqlite3_unlock(mailbox->m_sql);
4340  locked = 0;
4341 
4342  if( send_event ) {
4343  mailbox->m_cb(mailbox, MR_EVENT_CONTACTS_CHANGED, 0, 0);
4344  }
4345 
4346 cleanup:
4347  if( transaction_pending ) {
4348  mrsqlite3_rollback__(mailbox->m_sql);
4349  }
4350 
4351  if( locked ) {
4352  mrsqlite3_unlock(mailbox->m_sql);
4353  }
4354 
4355  mrcontact_unref(contact);
4356 }
4357 
4358 
4359 static void cat_fingerprint(mrstrbuilder_t* ret, const char* addr, const char* fingerprint_str)
4360 {
4361  mrstrbuilder_cat(ret, addr);
4362  mrstrbuilder_cat(ret, ":\n");
4363  mrstrbuilder_cat(ret, fingerprint_str);
4364  mrstrbuilder_cat(ret, "\n\n");
4365 }
4366 
4367 
4381 char* mrmailbox_get_contact_encrinfo(mrmailbox_t* mailbox, uint32_t contact_id)
4382 {
4383  int locked = 0;
4384  int e2ee_enabled = 0;
4385  int explain_id = 0;
4386  mrloginparam_t* loginparam = mrloginparam_new();
4387  mrcontact_t* contact = mrcontact_new();
4388  mrapeerstate_t* peerstate = mrapeerstate_new();
4389  int peerstate_ok = 0;
4390  mrkey_t* self_key = mrkey_new();
4391  char* fingerprint_str_self = NULL;
4392  char* fingerprint_str_other = NULL;
4393  char* p;
4394 
4395  mrstrbuilder_t ret;
4396  mrstrbuilder_init(&ret);
4397 
4398  mrsqlite3_lock(mailbox->m_sql);
4399  locked = 1;
4400 
4401  if( !mrcontact_load_from_db__(contact, mailbox->m_sql, contact_id) ) {
4402  goto cleanup;
4403  }
4404  peerstate_ok = mrapeerstate_load_from_db__(peerstate, mailbox->m_sql, contact->m_addr);
4405  mrloginparam_read__(loginparam, mailbox->m_sql, "configured_");
4406  e2ee_enabled = mailbox->m_e2ee_enabled;
4407 
4408  mrkey_load_self_public__(self_key, loginparam->m_addr, mailbox->m_sql);
4409 
4410  mrsqlite3_unlock(mailbox->m_sql);
4411  locked = 0;
4412 
4413  /* show the encryption that would be used for the next outgoing message */
4414  if( e2ee_enabled
4415  && peerstate_ok
4416  && peerstate->m_prefer_encrypt==MRA_PE_MUTUAL
4417  && peerstate->m_public_key->m_binary!=NULL )
4418  {
4419  /* e2e fine and used */
4420  p = mrstock_str(MR_STR_ENCR_E2E); mrstrbuilder_cat(&ret, p); free(p);
4421  explain_id = MR_STR_E2E_FINE;
4422  }
4423  else
4424  {
4425  /* e2e not used ... first, show status quo ... */
4426  if( !(loginparam->m_server_flags&MR_IMAP_SOCKET_PLAIN)
4427  && !(loginparam->m_server_flags&MR_SMTP_SOCKET_PLAIN) )
4428  {
4429  p = mrstock_str(MR_STR_ENCR_TRANSP); mrstrbuilder_cat(&ret, p); free(p);
4430  }
4431  else
4432  {
4433  p = mrstock_str(MR_STR_ENCR_NONE); mrstrbuilder_cat(&ret, p); free(p);
4434  }
4435 
4436  /* ... and then explain why we cannot use e2e */
4437  if( peerstate_ok && peerstate->m_public_key->m_binary!=NULL && peerstate->m_prefer_encrypt!=MRA_PE_MUTUAL ) {
4438  explain_id = MR_STR_E2E_DIS_BY_RCPT;
4439  }
4440  else if( !e2ee_enabled ) {
4441  explain_id = MR_STR_E2E_DIS_BY_YOU;
4442  }
4443  else {
4444  explain_id = MR_STR_E2E_NO_AUTOCRYPT;
4445  }
4446  }
4447 
4448  /* show fingerprints for comparison (sorted by email-address to make a device-side-by-side comparison easier) */
4449  if( peerstate_ok
4450  && peerstate->m_public_key->m_binary!=NULL )
4451  {
4452  if( self_key->m_binary == NULL ) {
4453  mrpgp_rand_seed(mailbox, peerstate->m_addr, strlen(peerstate->m_addr) /*just some random data*/);
4454  mrmailbox_ensure_secret_key_exists(mailbox);
4455  mrsqlite3_lock(mailbox->m_sql);
4456  locked = 1;
4457  mrkey_load_self_public__(self_key, loginparam->m_addr, mailbox->m_sql);
4458  mrsqlite3_unlock(mailbox->m_sql);
4459  locked = 0;
4460  }
4461 
4462  mrstrbuilder_cat(&ret, " ");
4463  p = mrstock_str(MR_STR_FINGERPRINTS); mrstrbuilder_cat(&ret, p); free(p);
4464  mrstrbuilder_cat(&ret, ":\n\n");
4465 
4466  fingerprint_str_self = mrkey_render_fingerprint(self_key, mailbox);
4467  fingerprint_str_other = mrkey_render_fingerprint(peerstate->m_public_key, mailbox);
4468 
4469  if( strcmp(loginparam->m_addr, peerstate->m_addr)<0 ) {
4470  cat_fingerprint(&ret, loginparam->m_addr, fingerprint_str_self);
4471  cat_fingerprint(&ret, peerstate->m_addr, fingerprint_str_other);
4472  }
4473  else {
4474  cat_fingerprint(&ret, peerstate->m_addr, fingerprint_str_other);
4475  cat_fingerprint(&ret, loginparam->m_addr, fingerprint_str_self);
4476  }
4477  }
4478  else
4479  {
4480  mrstrbuilder_cat(&ret, "\n\n");
4481  }
4482 
4483  p = mrstock_str(explain_id); mrstrbuilder_cat(&ret, p); free(p);
4484 
4485 cleanup:
4486  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
4487  mrapeerstate_unref(peerstate);
4488  mrcontact_unref(contact);
4489  mrloginparam_unref(loginparam);
4490  mrkey_unref(self_key);
4491  free(fingerprint_str_self);
4492  free(fingerprint_str_other);
4493  return ret.m_buf;
4494 }
4495 
4496 
4509 int mrmailbox_delete_contact(mrmailbox_t* mailbox, uint32_t contact_id)
4510 {
4511  int locked = 0, success = 0;
4512  sqlite3_stmt* stmt;
4513 
4514  if( mailbox == NULL || contact_id <= MR_CONTACT_ID_LAST_SPECIAL ) {
4515  goto cleanup;
4516  }
4517 
4518  mrsqlite3_lock(mailbox->m_sql);
4519  locked = 1;
4520 
4521  /* we can only delete contacts that are not in use anywhere; this function is mainly for the user who has just
4522  created an contact manually and wants to delete it a moment later */
4523  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_chats_contacts_WHERE_contact_id,
4524  "SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;");
4525  sqlite3_bind_int(stmt, 1, contact_id);
4526  if( sqlite3_step(stmt) != SQLITE_ROW || sqlite3_column_int(stmt, 0) >= 1 ) {
4527  goto cleanup;
4528  }
4529 
4530  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_WHERE_ft,
4531  "SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;");
4532  sqlite3_bind_int(stmt, 1, contact_id);
4533  sqlite3_bind_int(stmt, 2, contact_id);
4534  if( sqlite3_step(stmt) != SQLITE_ROW || sqlite3_column_int(stmt, 0) >= 1 ) {
4535  goto cleanup;
4536  }
4537 
4538  stmt = mrsqlite3_predefine__(mailbox->m_sql, DELETE_FROM_contacts_WHERE_id,
4539  "DELETE FROM contacts WHERE id=?;");
4540  sqlite3_bind_int(stmt, 1, contact_id);
4541  if( sqlite3_step(stmt) != SQLITE_DONE ) {
4542  goto cleanup;
4543  }
4544 
4545  mrsqlite3_unlock(mailbox->m_sql);
4546  locked = 0;
4547 
4548  mailbox->m_cb(mailbox, MR_EVENT_CONTACTS_CHANGED, 0, 0);
4549 
4550  success = 1;
4551 
4552 cleanup:
4553  if( locked ) {
4554  mrsqlite3_unlock(mailbox->m_sql);
4555  }
4556  return success;
4557 }
4558 
4559 
4560 int mrmailbox_contact_addr_equals__(mrmailbox_t* mailbox, uint32_t contact_id, const char* other_addr)
4561 {
4562  int addr_are_equal = 0;
4563  if( other_addr ) {
4564  mrcontact_t* contact = mrcontact_new();
4565  if( mrcontact_load_from_db__(contact, mailbox->m_sql, contact_id) ) {
4566  if( contact->m_addr ) {
4567  if( strcasecmp(contact->m_addr, other_addr)==0 ) {
4568  addr_are_equal = 1;
4569  }
4570  }
4571  }
4572  mrcontact_unref(contact);
4573  }
4574  return addr_are_equal;
4575 }
4576 
4577 
4578 
4579 /*******************************************************************************
4580  * Handle Messages
4581  ******************************************************************************/
4582 
4583 
4584 void mrmailbox_update_msg_chat_id__(mrmailbox_t* mailbox, uint32_t msg_id, uint32_t chat_id)
4585 {
4586  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_chat_id_WHERE_id,
4587  "UPDATE msgs SET chat_id=? WHERE id=?;");
4588  sqlite3_bind_int(stmt, 1, chat_id);
4589  sqlite3_bind_int(stmt, 2, msg_id);
4590  sqlite3_step(stmt);
4591 }
4592 
4593 
4594 void mrmailbox_update_msg_state__(mrmailbox_t* mailbox, uint32_t msg_id, int state)
4595 {
4596  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_state_WHERE_id,
4597  "UPDATE msgs SET state=? WHERE id=?;");
4598  sqlite3_bind_int(stmt, 1, state);
4599  sqlite3_bind_int(stmt, 2, msg_id);
4600  sqlite3_step(stmt);
4601 }
4602 
4603 
4604 size_t mrmailbox_get_real_msg_cnt__(mrmailbox_t* mailbox)
4605 {
4606  if( mailbox->m_sql->m_cobj==NULL ) {
4607  return 0;
4608  }
4609 
4610  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_WHERE_assigned,
4611  "SELECT COUNT(*) FROM msgs WHERE id>? AND chat_id>?;");
4612  sqlite3_bind_int(stmt, 1, MR_MSG_ID_LAST_SPECIAL);
4613  sqlite3_bind_int(stmt, 2, MR_CHAT_ID_LAST_SPECIAL);
4614  if( sqlite3_step(stmt) != SQLITE_ROW ) {
4615  mrsqlite3_log_error(mailbox->m_sql, "mr_get_assigned_msg_cnt_() failed.");
4616  return 0;
4617  }
4618 
4619  return sqlite3_column_int(stmt, 0);
4620 }
4621 
4622 
4623 size_t mrmailbox_get_deaddrop_msg_cnt__(mrmailbox_t* mailbox)
4624 {
4625  if( mailbox==NULL || mailbox->m_sql->m_cobj==NULL ) {
4626  return 0;
4627  }
4628 
4629  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_WHERE_unassigned,
4630  "SELECT COUNT(*) FROM msgs WHERE chat_id=?;");
4631  sqlite3_bind_int(stmt, 1, MR_CHAT_ID_DEADDROP);
4632  if( sqlite3_step(stmt) != SQLITE_ROW ) {
4633  return 0;
4634  }
4635 
4636  return sqlite3_column_int(stmt, 0);
4637 }
4638 
4639 
4640 int mrmailbox_rfc724_mid_cnt__(mrmailbox_t* mailbox, const char* rfc724_mid)
4641 {
4642  if( mailbox==NULL || mailbox->m_sql->m_cobj==NULL ) {
4643  return 0;
4644  }
4645 
4646  /* check the number of messages with the same rfc724_mid */
4647  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_WHERE_rfc724_mid,
4648  "SELECT COUNT(*) FROM msgs WHERE rfc724_mid=?;");
4649  sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
4650  if( sqlite3_step(stmt) != SQLITE_ROW ) {
4651  return 0;
4652  }
4653 
4654  return sqlite3_column_int(stmt, 0);
4655 }
4656 
4657 
4658 /* check, if the given Message-ID exists in the database (if not, the message is normally downloaded from the server and parsed,
4659 so, we should even keep unuseful messages in the database (we can leave the other fields empty to safe space) */
4660 int mrmailbox_rfc724_mid_exists__(mrmailbox_t* mailbox, const char* rfc724_mid, char** ret_server_folder, uint32_t* ret_server_uid)
4661 {
4662  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_ss_FROM_msgs_WHERE_m,
4663  "SELECT server_folder, server_uid FROM msgs WHERE rfc724_mid=?;");
4664  sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
4665  if( sqlite3_step(stmt) != SQLITE_ROW ) {
4666  *ret_server_folder = NULL;
4667  *ret_server_uid = 0;
4668  return 0;
4669  }
4670 
4671  *ret_server_folder = safe_strdup((char*)sqlite3_column_text(stmt, 0));
4672  *ret_server_uid = sqlite3_column_int(stmt, 1); /* may be 0 */
4673  return 1;
4674 }
4675 
4676 
4677 void mrmailbox_update_server_uid__(mrmailbox_t* mailbox, const char* rfc724_mid, const char* server_folder, uint32_t server_uid)
4678 {
4679  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_ss_WHERE_rfc724_mid,
4680  "UPDATE msgs SET server_folder=?, server_uid=? WHERE rfc724_mid=?;"); /* we update by "rfc724_mid" instead "id" as there may be several db-entries refering to the same "rfc724_mid" */
4681  sqlite3_bind_text(stmt, 1, server_folder, -1, SQLITE_STATIC);
4682  sqlite3_bind_int (stmt, 2, server_uid);
4683  sqlite3_bind_text(stmt, 3, rfc724_mid, -1, SQLITE_STATIC);
4684  sqlite3_step(stmt);
4685 }
4686 
4687 
4701 mrmsg_t* mrmailbox_get_msg(mrmailbox_t* mailbox, uint32_t msg_id)
4702 {
4703  int success = 0;
4704  int db_locked = 0;
4705  mrmsg_t* obj = mrmsg_new();
4706 
4707  mrsqlite3_lock(mailbox->m_sql);
4708  db_locked = 1;
4709 
4710  if( !mrmsg_load_from_db__(obj, mailbox, msg_id) ) {
4711  goto cleanup;
4712  }
4713 
4714  success = 1;
4715 
4716 cleanup:
4717  if( db_locked ) {
4718  mrsqlite3_unlock(mailbox->m_sql);
4719  }
4720 
4721  if( success ) {
4722  return obj;
4723  }
4724  else {
4725  mrmsg_unref(obj);
4726  return NULL;
4727  }
4728 }
4729 
4730 
4743 char* mrmailbox_get_msg_info(mrmailbox_t* mailbox, uint32_t msg_id)
4744 {
4745  mrstrbuilder_t ret;
4746  int locked = 0;
4747  sqlite3_stmt* stmt;
4748  mrmsg_t* msg = mrmsg_new();
4749  char *rawtxt = NULL, *p;
4750 
4751  mrstrbuilder_init(&ret);
4752 
4753  if( mailbox == NULL ) {
4754  goto cleanup;
4755  }
4756 
4757  mrsqlite3_lock(mailbox->m_sql);
4758  locked = 1;
4759 
4760  mrmsg_load_from_db__(msg, mailbox, msg_id);
4761 
4762  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_txt_raw_FROM_msgs_WHERE_id,
4763  "SELECT txt_raw FROM msgs WHERE id=?;");
4764  sqlite3_bind_int(stmt, 1, msg_id);
4765  if( sqlite3_step(stmt) != SQLITE_ROW ) {
4766  p = mr_mprintf("Cannot load message #%i.", (int)msg_id); mrstrbuilder_cat(&ret, p); free(p);
4767  goto cleanup;
4768  }
4769 
4770  rawtxt = safe_strdup((char*)sqlite3_column_text(stmt, 0));
4771 
4772  mrsqlite3_unlock(mailbox->m_sql);
4773  locked = 0;
4774 
4775  /* add time */
4776  mrstrbuilder_cat(&ret, "Date: ");
4777  p = mr_timestamp_to_str(msg->m_timestamp); mrstrbuilder_cat(&ret, p); free(p);
4778  mrstrbuilder_cat(&ret, "\n");
4779 
4780  /* add encryption state */
4781  int e2ee_errors;
4782  if( (e2ee_errors=mrparam_get_int(msg->m_param, MRP_ERRONEOUS_E2EE, 0)) ) {
4783  if( e2ee_errors&MR_VALIDATE_BAD_SIGNATURE/* check worst errors first */ ) {
4784  p = safe_strdup("End-to-end, bad signature");
4785  }
4786  else if( e2ee_errors&MR_VALIDATE_UNKNOWN_SIGNATURE ) {
4787  p = safe_strdup("End-to-end, unknown signature");
4788  }
4789  else if( e2ee_errors&MR_VALIDATE_NOT_MUTUAL ) {
4790  p = safe_strdup("End-to-end, not mutual");
4791  }
4792  else {
4793  p = safe_strdup("End-to-end, no signature");
4794  }
4795  }
4796  else if( mrparam_get_int(msg->m_param, MRP_GUARANTEE_E2EE, 0) ) {
4797  if( !msg->m_mailbox->m_e2ee_enabled ) {
4798  p = safe_strdup("End-to-end, transport for replies");
4799  }
4800  else {
4801  p = safe_strdup("End-to-end");
4802  }
4803  }
4804  else {
4805  p = safe_strdup("Transport");
4806  }
4807  mrstrbuilder_cat(&ret, "Encryption: ");
4808  mrstrbuilder_cat(&ret, p); free(p);
4809  mrstrbuilder_cat(&ret, "\n");
4810 
4811  /* add "suspicious" status */
4812  if( msg->m_state==MR_STATE_IN_FRESH ) {
4813  mrstrbuilder_cat(&ret, "Status: Fresh\n");
4814  }
4815  else if( msg->m_state==MR_STATE_IN_NOTICED ) {
4816  mrstrbuilder_cat(&ret, "Status: Noticed\n");
4817  }
4818 
4819  /* add file info */
4820  char* file = mrparam_get(msg->m_param, MRP_FILE, NULL);
4821  if( file ) {
4822  p = mr_mprintf("File: %s, %i bytes\n", file, mr_get_filebytes(file)); mrstrbuilder_cat(&ret, p); free(p);
4823  }
4824 
4825  if( msg->m_type != MR_MSG_TEXT ) {
4826  p = mr_mprintf("Type: %i\n", msg->m_type); mrstrbuilder_cat(&ret, p); free(p);
4827  }
4828 
4829  int w = mrparam_get_int(msg->m_param, MRP_WIDTH, 0), h = mrparam_get_int(msg->m_param, MRP_HEIGHT, 0);
4830  if( w != 0 || h != 0 ) {
4831  p = mr_mprintf("Dimension: %i x %i\n", w, h); mrstrbuilder_cat(&ret, p); free(p);
4832  }
4833 
4834  int duration = mrparam_get_int(msg->m_param, MRP_DURATION, 0);
4835  if( duration != 0 ) {
4836  p = mr_mprintf("Duration: %i ms\n", duration); mrstrbuilder_cat(&ret, p); free(p);
4837  }
4838 
4839  /* add rawtext */
4840  if( rawtxt && rawtxt[0] ) {
4841  mrstrbuilder_cat(&ret, "\n");
4842  mrstrbuilder_cat(&ret, rawtxt);
4843  }
4844 
4845 cleanup:
4846  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
4847  mrmsg_unref(msg);
4848  free(rawtxt);
4849  return ret.m_buf;
4850 }
4851 
4852 
4868 void mrmailbox_forward_msgs(mrmailbox_t* mailbox, const uint32_t* msg_ids, int msg_cnt, uint32_t chat_id)
4869 {
4870  mrmsg_t* msg = mrmsg_new();
4871  mrchat_t* chat = mrchat_new(mailbox);
4872  mrcontact_t* contact = mrcontact_new();
4873  int locked = 0, transaction_pending = 0;
4874  carray* created_db_entries = carray_new(16);
4875  char* idsstr = NULL, *q3 = NULL;
4876  sqlite3_stmt* stmt = NULL;
4877  time_t curr_timestamp;
4878 
4879  if( mailbox == NULL || msg_ids==NULL || msg_cnt <= 0 || chat_id <= MR_CHAT_ID_LAST_SPECIAL ) {
4880  goto cleanup;
4881  }
4882 
4883  mrsqlite3_lock(mailbox->m_sql);
4884  locked = 1;
4885  mrsqlite3_begin_transaction__(mailbox->m_sql);
4886  transaction_pending = 1;
4887 
4888  mrmailbox_unarchive_chat__(mailbox, chat_id);
4889 
4890  mailbox->m_smtp->m_log_connect_errors = 1;
4891 
4892  if( !mrchat_load_from_db__(chat, chat_id) ) {
4893  goto cleanup;
4894  }
4895 
4896  curr_timestamp = mr_create_smeared_timestamps__(msg_cnt);
4897 
4898  idsstr = mr_arr_to_string(msg_ids, msg_cnt);
4899  q3 = sqlite3_mprintf("SELECT id FROM msgs WHERE id IN(%s) ORDER BY timestamp,id", idsstr);
4900  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, q3);
4901  while( sqlite3_step(stmt)==SQLITE_ROW )
4902  {
4903  int src_msg_id = sqlite3_column_int(stmt, 0);
4904  if( !mrmsg_load_from_db__(msg, mailbox, src_msg_id) ) {
4905  goto cleanup;
4906  }
4907 
4908  mrparam_set_int(msg->m_param, MRP_FORWARDED, 1);
4909 
4910  uint32_t new_msg_id = mrmailbox_send_msg_i__(mailbox, chat, msg, curr_timestamp++);
4911  carray_add(created_db_entries, (void*)(uintptr_t)chat_id, NULL);
4912  carray_add(created_db_entries, (void*)(uintptr_t)new_msg_id, NULL);
4913  }
4914 
4915  mrsqlite3_commit__(mailbox->m_sql);
4916  transaction_pending = 0;
4917 
4918 cleanup:
4919  if( transaction_pending ) { mrsqlite3_rollback__(mailbox->m_sql); }
4920  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
4921  if( created_db_entries ) {
4922  size_t i, icnt = carray_count(created_db_entries);
4923  for( i = 0; i < icnt; i += 2 ) {
4924  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, (uintptr_t)carray_get(created_db_entries, i), (uintptr_t)carray_get(created_db_entries, i+1));
4925  }
4926  carray_free(created_db_entries);
4927  }
4928  mrcontact_unref(contact);
4929  mrmsg_unref(msg);
4930  mrchat_unref(chat);
4931  if( stmt ) { sqlite3_finalize(stmt); }
4932  free(idsstr);
4933  if( q3 ) { sqlite3_free(q3); }
4934 }
4935 
4936 
4954 void mrmailbox_star_msgs(mrmailbox_t* mailbox, const uint32_t* msg_ids, int msg_cnt, int star)
4955 {
4956  int i;
4957 
4958  if( mailbox == NULL || msg_ids == NULL || msg_cnt <= 0 || (star!=0 && star!=1) ) {
4959  return;
4960  }
4961 
4962  mrsqlite3_lock(mailbox->m_sql);
4963  mrsqlite3_begin_transaction__(mailbox->m_sql);
4964 
4965  for( i = 0; i < msg_cnt; i++ )
4966  {
4967  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_starred_WHERE_id,
4968  "UPDATE msgs SET starred=? WHERE id=?;");
4969  sqlite3_bind_int(stmt, 1, star);
4970  sqlite3_bind_int(stmt, 2, msg_ids[i]);
4971  sqlite3_step(stmt);
4972  }
4973 
4974  mrsqlite3_commit__(mailbox->m_sql);
4975  mrsqlite3_unlock(mailbox->m_sql);
4976 }
4977 
4978 
4979 /*******************************************************************************
4980  * Delete messages
4981  ******************************************************************************/
4982 
4983 
4984 /* internal function */
4985 void mrmailbox_delete_msg_on_imap(mrmailbox_t* mailbox, mrjob_t* job)
4986 {
4987  int locked = 0, delete_from_server = 1;
4988  mrmsg_t* msg = mrmsg_new();
4989 
4990  mrsqlite3_lock(mailbox->m_sql);
4991  locked = 1;
4992 
4993  if( !mrmsg_load_from_db__(msg, mailbox, job->m_foreign_id) ) {
4994  goto cleanup;
4995  }
4996 
4997  if( mrmailbox_rfc724_mid_cnt__(mailbox, msg->m_rfc724_mid) != 1 ) {
4998  mrmailbox_log_info(mailbox, 0, "The message is deleted from the server when all message are deleted.");
4999  delete_from_server = 0;
5000  }
5001 
5002  mrsqlite3_unlock(mailbox->m_sql);
5003  locked = 0;
5004 
5005  /* if this is the last existing part of the message, we delete the message from the server */
5006  if( delete_from_server )
5007  {
5008  if( !mrimap_is_connected(mailbox->m_imap) ) {
5009  mrmailbox_connect_to_imap(mailbox, NULL);
5010  if( !mrimap_is_connected(mailbox->m_imap) ) {
5011  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5012  goto cleanup;
5013  }
5014  }
5015 
5016  if( !mrimap_delete_msg(mailbox->m_imap, msg->m_rfc724_mid, msg->m_server_folder, msg->m_server_uid) )
5017  {
5018  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5019  goto cleanup;
5020  }
5021  }
5022 
5023  /* we delete the database entry ...
5024  - if the message is successfully removed from the server
5025  - or if there are other parts of the messages in the database (in this case we have not deleted if from the server)
5026  (As long as the message is not removed from the IMAP-server, we need at least one database entry to avoid a re-download) */
5027  mrsqlite3_lock(mailbox->m_sql);
5028  locked = 1;
5029 
5030  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, DELETE_FROM_msgs_WHERE_id, "DELETE FROM msgs WHERE id=?;");
5031  sqlite3_bind_int(stmt, 1, msg->m_id);
5032  sqlite3_step(stmt);
5033 
5034  char* pathNfilename = mrparam_get(msg->m_param, MRP_FILE, NULL);
5035  if( pathNfilename ) {
5036  if( strncmp(mailbox->m_blobdir, pathNfilename, strlen(mailbox->m_blobdir))==0 )
5037  {
5038  char* strLikeFilename = mr_mprintf("%%f=%s%%", pathNfilename);
5039  sqlite3_stmt* stmt2 = mrsqlite3_prepare_v2_(mailbox->m_sql, "SELECT id FROM msgs WHERE type!=? AND param LIKE ?;"); /* if this gets too slow, an index over "type" should help. */
5040  sqlite3_bind_int (stmt2, 1, MR_MSG_TEXT);
5041  sqlite3_bind_text(stmt2, 2, strLikeFilename, -1, SQLITE_STATIC);
5042  int file_used_by_other_msgs = (sqlite3_step(stmt2)==SQLITE_ROW)? 1 : 0;
5043  free(strLikeFilename);
5044  sqlite3_finalize(stmt2);
5045 
5046  if( !file_used_by_other_msgs )
5047  {
5048  mr_delete_file(pathNfilename, mailbox);
5049 
5050  char* increation_file = mr_mprintf("%s.increation", pathNfilename);
5051  mr_delete_file(increation_file, mailbox);
5052  free(increation_file);
5053 
5054  char* filenameOnly = mr_get_filename(pathNfilename);
5055  if( msg->m_type==MR_MSG_VOICE ) {
5056  char* waveform_file = mr_mprintf("%s/%s.waveform", mailbox->m_blobdir, filenameOnly);
5057  mr_delete_file(waveform_file, mailbox);
5058  free(waveform_file);
5059  }
5060  else if( msg->m_type==MR_MSG_VIDEO ) {
5061  char* preview_file = mr_mprintf("%s/%s-preview.jpg", mailbox->m_blobdir, filenameOnly);
5062  mr_delete_file(preview_file, mailbox);
5063  free(preview_file);
5064  }
5065  free(filenameOnly);
5066  }
5067  }
5068  free(pathNfilename);
5069  }
5070 
5071  mrsqlite3_unlock(mailbox->m_sql);
5072  locked = 0;
5073 
5074 cleanup:
5075  if( locked ) {
5076  mrsqlite3_unlock(mailbox->m_sql);
5077  }
5078  mrmsg_unref(msg);
5079 }
5080 
5081 
5096 void mrmailbox_delete_msgs(mrmailbox_t* mailbox, const uint32_t* msg_ids, int msg_cnt)
5097 {
5098  int i;
5099 
5100  if( mailbox == NULL || msg_ids == NULL || msg_cnt <= 0 ) {
5101  return;
5102  }
5103 
5104  mrsqlite3_lock(mailbox->m_sql);
5105  mrsqlite3_begin_transaction__(mailbox->m_sql);
5106 
5107  for( i = 0; i < msg_cnt; i++ )
5108  {
5109  mrmailbox_update_msg_chat_id__(mailbox, msg_ids[i], MR_CHAT_ID_TRASH);
5110  mrjob_add__(mailbox, MRJ_DELETE_MSG_ON_IMAP, msg_ids[i], NULL); /* results in a call to mrmailbox_delete_msg_on_imap() */
5111  }
5112 
5113  mrsqlite3_commit__(mailbox->m_sql);
5114  mrsqlite3_unlock(mailbox->m_sql);
5115 }
5116 
5117 
5118 /*******************************************************************************
5119  * mark message as seen
5120  ******************************************************************************/
5121 
5122 
5123 void mrmailbox_markseen_msg_on_imap(mrmailbox_t* mailbox, mrjob_t* job)
5124 {
5125  int locked = 0;
5126  mrmsg_t* msg = mrmsg_new();
5127  char* new_server_folder = NULL;
5128  uint32_t new_server_uid = 0;
5129  int in_ms_flags = 0, out_ms_flags = 0;
5130 
5131  if( !mrimap_is_connected(mailbox->m_imap) ) {
5132  mrmailbox_connect_to_imap(mailbox, NULL);
5133  if( !mrimap_is_connected(mailbox->m_imap) ) {
5134  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5135  goto cleanup;
5136  }
5137  }
5138 
5139  mrsqlite3_lock(mailbox->m_sql);
5140  locked = 1;
5141 
5142  if( !mrmsg_load_from_db__(msg, mailbox, job->m_foreign_id) ) {
5143  goto cleanup;
5144  }
5145 
5146  /* add an additional job for sending the MDN (here in a thread for fast ui resonses) (an extra job as the MDN has a lower priority) */
5147  if( mrparam_get_int(msg->m_param, MRP_WANTS_MDN, 0) /* MRP_WANTS_MDN is set only for one part of a multipart-message */
5148  && mrsqlite3_get_config_int__(mailbox->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED) ) {
5149  in_ms_flags |= MR_MS_SET_MDNSent_FLAG;
5150  }
5151 
5152  mrsqlite3_unlock(mailbox->m_sql);
5153  locked = 0;
5154 
5155  if( msg->m_is_msgrmsg ) {
5156  in_ms_flags |= MR_MS_ALSO_MOVE;
5157  }
5158 
5159  if( mrimap_markseen_msg(mailbox->m_imap, msg->m_server_folder, msg->m_server_uid,
5160  in_ms_flags, &new_server_folder, &new_server_uid, &out_ms_flags) != 0 )
5161  {
5162  if( (new_server_folder && new_server_uid) || out_ms_flags&MR_MS_MDNSent_JUST_SET )
5163  {
5164  mrsqlite3_lock(mailbox->m_sql);
5165  locked = 1;
5166 
5167  if( new_server_folder && new_server_uid )
5168  {
5169  mrmailbox_update_server_uid__(mailbox, msg->m_rfc724_mid, new_server_folder, new_server_uid);
5170  }
5171 
5172  if( out_ms_flags&MR_MS_MDNSent_JUST_SET )
5173  {
5174  mrjob_add__(mailbox, MRJ_SEND_MDN, msg->m_id, NULL); /* results in a call to mrmailbox_send_mdn() */
5175  }
5176 
5177  mrsqlite3_unlock(mailbox->m_sql);
5178  locked = 0;
5179  }
5180  }
5181  else
5182  {
5183  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5184  }
5185 
5186 cleanup:
5187  if( locked ) {
5188  mrsqlite3_unlock(mailbox->m_sql);
5189  }
5190  mrmsg_unref(msg);
5191  free(new_server_folder);
5192 }
5193 
5194 
5195 void mrmailbox_markseen_mdn_on_imap(mrmailbox_t* mailbox, mrjob_t* job)
5196 {
5197  char* server_folder = mrparam_get (job->m_param, MRP_SERVER_FOLDER, NULL);
5198  uint32_t server_uid = mrparam_get_int(job->m_param, MRP_SERVER_UID, 0);
5199  char* new_server_folder = NULL;
5200  uint32_t new_server_uid = 0;
5201  int out_ms_flags = 0;
5202 
5203  if( !mrimap_is_connected(mailbox->m_imap) ) {
5204  mrmailbox_connect_to_imap(mailbox, NULL);
5205  if( !mrimap_is_connected(mailbox->m_imap) ) {
5206  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5207  goto cleanup;
5208  }
5209  }
5210 
5211  if( mrimap_markseen_msg(mailbox->m_imap, server_folder, server_uid, MR_MS_ALSO_MOVE, &new_server_folder, &new_server_uid, &out_ms_flags) == 0 ) {
5212  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5213  }
5214 
5215 cleanup:
5216  free(server_folder);
5217  free(new_server_folder);
5218 }
5219 
5220 
5237 void mrmailbox_markseen_msgs(mrmailbox_t* mailbox, const uint32_t* msg_ids, int msg_cnt)
5238 {
5239  int i, send_event = 0;
5240 
5241  if( mailbox == NULL || msg_ids == NULL || msg_cnt <= 0 ) {
5242  return;
5243  }
5244 
5245  mrsqlite3_lock(mailbox->m_sql);
5246  mrsqlite3_begin_transaction__(mailbox->m_sql);
5247 
5248  for( i = 0; i < msg_cnt; i++ )
5249  {
5250  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_seen_WHERE_id_AND_chat_id_AND_freshORnoticed,
5251  "UPDATE msgs SET state=" MR_STRINGIFY(MR_STATE_IN_SEEN)
5252  " WHERE id=? AND chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) " AND (state=" MR_STRINGIFY(MR_STATE_IN_FRESH) " OR state=" MR_STRINGIFY(MR_STATE_IN_NOTICED) ");");
5253  sqlite3_bind_int(stmt, 1, msg_ids[i]);
5254  sqlite3_step(stmt);
5255  if( sqlite3_changes(mailbox->m_sql->m_cobj) )
5256  {
5257  mrmailbox_log_info(mailbox, 0, "Seen message #%i.", msg_ids[i]);
5258  mrjob_add__(mailbox, MRJ_MARKSEEN_MSG_ON_IMAP, msg_ids[i], NULL); /* results in a call to mrmailbox_markseen_msg_on_imap() */
5259  send_event = 1;
5260  }
5261  else
5262  {
5263  /* message may be in contact requests, mark as NOTICED, this does not force IMAP updated nor send MDNs */
5264  sqlite3_stmt* stmt2 = mrsqlite3_predefine__(mailbox->m_sql, UPDATE_msgs_SET_noticed_WHERE_id_AND_fresh,
5265  "UPDATE msgs SET state=" MR_STRINGIFY(MR_STATE_IN_NOTICED)
5266  " WHERE id=? AND state=" MR_STRINGIFY(MR_STATE_IN_FRESH) ";");
5267  sqlite3_bind_int(stmt2, 1, msg_ids[i]);
5268  sqlite3_step(stmt2);
5269  if( sqlite3_changes(mailbox->m_sql->m_cobj) ) {
5270  send_event = 1;
5271  }
5272  }
5273  }
5274 
5275  mrsqlite3_commit__(mailbox->m_sql);
5276  mrsqlite3_unlock(mailbox->m_sql);
5277 
5278  /* the event us needed eg. to remove the deaddrop from the chatlist */
5279  if( send_event ) {
5280  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, 0, 0);
5281  }
5282 }
5283 
5284 
5285 int mrmailbox_mdn_from_ext__(mrmailbox_t* mailbox, uint32_t from_id, const char* rfc724_mid,
5286  uint32_t* ret_chat_id,
5287  uint32_t* ret_msg_id)
5288 {
5289  if( mailbox == NULL || from_id <= MR_CONTACT_ID_LAST_SPECIAL || rfc724_mid == NULL || ret_chat_id==NULL || ret_msg_id==NULL
5290  || *ret_chat_id != 0 || *ret_msg_id != 0 ) {
5291  return 0;
5292  }
5293 
5294  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_it_FROM_msgs_JOIN_chats_WHERE_rfc724,
5295  "SELECT m.id, c.id, c.type, m.state FROM msgs m "
5296  " LEFT JOIN chats c ON m.chat_id=c.id "
5297  " WHERE rfc724_mid=? AND from_id=1 "
5298  " ORDER BY m.id;"); /* the ORDER BY makes sure, if one rfc724_mid is splitted into its parts, we always catch the same one. However, we do not send multiparts, we do not request MDNs for multiparts, and should not receive read requests for multiparts. So this is currently more theoretical. */
5299  sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
5300  if( sqlite3_step(stmt) != SQLITE_ROW ) {
5301  return 0;
5302  }
5303 
5304  *ret_msg_id = sqlite3_column_int(stmt, 0);
5305  *ret_chat_id = sqlite3_column_int(stmt, 1);
5306  int chat_type = sqlite3_column_int(stmt, 2);
5307  int msg_state = sqlite3_column_int(stmt, 3);
5308 
5309  if( msg_state!=MR_STATE_OUT_PENDING && msg_state!=MR_STATE_OUT_DELIVERED ) {
5310  return 0; /* eg. already marked as MDNS_RCVD. however, it is importent, that the message ID is set above as this will allow the caller eg. to move the message away */
5311  }
5312 
5313  /* normal chat? that's quite easy. */
5314  if( chat_type == MR_CHAT_TYPE_NORMAL )
5315  {
5316  mrmailbox_update_msg_state__(mailbox, *ret_msg_id, MR_STATE_OUT_MDN_RCVD);
5317  return 1; /* send event about new state */
5318  }
5319 
5320  /* group chat: collect receipt senders */
5321  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_c_FROM_msgs_mdns_WHERE_mc, "SELECT contact_id FROM msgs_mdns WHERE msg_id=? AND contact_id=?;");
5322  sqlite3_bind_int(stmt, 1, *ret_msg_id);
5323  sqlite3_bind_int(stmt, 2, from_id);
5324  if( sqlite3_step(stmt) != SQLITE_ROW ) {
5325  stmt = mrsqlite3_predefine__(mailbox->m_sql, INSERT_INTO_msgs_mdns, "INSERT INTO msgs_mdns (msg_id, contact_id) VALUES (?, ?);");
5326  sqlite3_bind_int(stmt, 1, *ret_msg_id);
5327  sqlite3_bind_int(stmt, 2, from_id);
5328  sqlite3_step(stmt);
5329  }
5330 
5331  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_COUNT_FROM_msgs_mdns_WHERE_m, "SELECT COUNT(*) FROM msgs_mdns WHERE msg_id=?;");
5332  sqlite3_bind_int(stmt, 1, *ret_msg_id);
5333  if( sqlite3_step(stmt) != SQLITE_ROW ) {
5334  return 0; /* error */
5335  }
5336 
5337  /*
5338  Groupsize: Min. MDNs
5339 
5340  1 S n/a
5341  2 SR 1
5342  3 SRR 2
5343  4 SRRR 2
5344  5 SRRRR 3
5345  6 SRRRRR 3
5346 
5347  (S=Sender, R=Recipient)
5348  */
5349  int ist_cnt = sqlite3_column_int(stmt, 0);
5350  int soll_cnt = (mrmailbox_get_chat_contact_count__(mailbox, *ret_chat_id)+1/*for rounding, SELF is already included!*/) / 2;
5351  if( ist_cnt < soll_cnt ) {
5352  return 0; /* wait for more receipts */
5353  }
5354 
5355  /* got enough receipts :-) */
5356  stmt = mrsqlite3_predefine__(mailbox->m_sql, DELETE_FROM_msgs_mdns_WHERE_m, "DELETE FROM msgs_mdns WHERE msg_id=?;");
5357  sqlite3_bind_int(stmt, 1, *ret_msg_id);
5358  sqlite3_step(stmt);
5359 
5360  mrmailbox_update_msg_state__(mailbox, *ret_msg_id, MR_STATE_OUT_MDN_RCVD);
5361  return 1;
5362 }
5363 
5364 
5365 void mrmailbox_send_mdn(mrmailbox_t* mailbox, mrjob_t* job)
5366 {
5367  mrmimefactory_t mimefactory;
5368  mrmimefactory_init(&mimefactory, mailbox);
5369 
5370  if( mailbox == NULL || job == NULL ) {
5371  return;
5372  }
5373 
5374  /* connect to SMTP server, if not yet done */
5375  if( !mrsmtp_is_connected(mailbox->m_smtp) ) {
5376  mrloginparam_t* loginparam = mrloginparam_new();
5377  mrsqlite3_lock(mailbox->m_sql);
5378  mrloginparam_read__(loginparam, mailbox->m_sql, "configured_");
5379  mrsqlite3_unlock(mailbox->m_sql);
5380  int connected = mrsmtp_connect(mailbox->m_smtp, loginparam);
5381  mrloginparam_unref(loginparam);
5382  if( !connected ) {
5383  mrjob_try_again_later(job, MR_STANDARD_DELAY);
5384  goto cleanup;
5385  }
5386  }
5387 
5388  if( !mrmimefactory_load_mdn(&mimefactory, job->m_foreign_id)
5389  || !mrmimefactory_render(&mimefactory, 0/*encrypt to self*/) ) {
5390  goto cleanup;
5391  }
5392 
5393  //char* t1=mr_null_terminate(mimefactory.m_out->str,mimefactory.m_out->len);printf("~~~~~MDN~~~~~\n%s\n~~~~~/MDN~~~~~",t1);free(t1); // DEBUG OUTPUT
5394 
5395  if( !mrsmtp_send_msg(mailbox->m_smtp, mimefactory.m_recipients_addr, mimefactory.m_out->str, mimefactory.m_out->len) ) {
5396  mrsmtp_disconnect(mailbox->m_smtp);
5397  mrjob_try_again_later(job, MR_AT_ONCE); /* MR_AT_ONCE is only the _initial_ delay, if the second try failes, the delay gets larger */
5398  goto cleanup;
5399  }
5400 
5401 cleanup:
5402  mrmimefactory_empty(&mimefactory);
5403 }
5404 
int m_is_msgrmsg
Set to 1 if the message was sent by another messenger.
Definition: mrmsg.h:75
-
int mrmailbox_add_address_book(mrmailbox_t *mailbox, const char *adr_book)
Add a number of contacts.
Definition: mrmailbox.c:4021
-
uint32_t mrmailbox_create_group_chat(mrmailbox_t *mailbox, const char *chat_name)
Create a new group chat.
Definition: mrmailbox.c:3333
-
uint32_t mrmailbox_create_contact(mrmailbox_t *mailbox, const char *name, const char *addr)
Add a single contact.
Definition: mrmailbox.c:3985
-
uint32_t mrmailbox_send_text_msg(mrmailbox_t *mailbox, uint32_t chat_id, const char *text_to_send)
Send a simple text message to the given chat.
Definition: mrmailbox.c:3101
-
void mrmailbox_close(mrmailbox_t *mailbox)
Close mailbox database.
Definition: mrmailbox.c:1075
-
mrmailbox_t * mrmailbox_new(mrmailboxcb_t cb, void *userdata, const char *os_name)
Create a new mailbox object.
Definition: mrmailbox.c:897
-
int mrmailbox_is_contact_in_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Check if a given contact ID is a member of a group chat.
Definition: mrmailbox.c:3565
-
mrchat_t * mrmailbox_get_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Get a chat object of type mrchat_t by a chat_id.
Definition: mrmailbox.c:1709
-
int mrmailbox_remove_contact_from_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Remove a member from a group.
Definition: mrmailbox.c:3678
-
int mrmailbox_open(mrmailbox_t *mailbox, const char *dbfile, const char *blobdir)
Open mailbox database.
Definition: mrmailbox.c:1010
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
int mrmailbox_delete_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Delete a contact.
Definition: mrmailbox.c:4509
-
An object representing a single chatlist in memory.
Definition: mrchatlist.h:42
-
carray * mrmailbox_get_chat_msgs(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t flags, uint32_t marker1before)
Get all message IDs belonging to a chat.
Definition: mrmailbox.c:2120
-
void mrmailbox_star_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt, int star)
Star/unstar messages by setting the last parameter to 0 (unstar) or 1(star).
Definition: mrmailbox.c:4954
-
carray * mrmailbox_search_msgs(mrmailbox_t *mailbox, uint32_t chat_id, const char *query)
Search messages containing the given query string.
Definition: mrmailbox.c:2224
-
int mrmailbox_set_config(mrmailbox_t *ths, const char *key, const char *value)
Configure the mailbox.
Definition: mrmailbox.c:1176
-
char * m_dbfile
the database file in file.
Definition: mrmailbox.h:144
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrmailbox_delete_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt)
Delete a list of messages.
Definition: mrmailbox.c:5096
-
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
-
int mrmailbox_is_open(const mrmailbox_t *mailbox)
Check if a given mailbox database is open.
Definition: mrmailbox.c:1109
-
void mrmailbox_archive_chat(mrmailbox_t *mailbox, uint32_t chat_id, int archive)
Archive or unarchive a chat.
Definition: mrmailbox.c:2652
-
char * mrmailbox_get_info(mrmailbox_t *mailbox)
Get information about the mailbox.
Definition: mrmailbox.c:1292
-
int m_type
Message type as one of the MR_MSG_* contstants.
Definition: mrmsg.h:60
-
uint32_t m_chat_id
Chat ID the message belongs to.
Definition: mrmsg.h:49
-
int mrmailbox_add_contact_to_chat(mrmailbox_t *mailbox, uint32_t chat_id, uint32_t contact_id)
Add a member to a group.
Definition: mrmailbox.c:3595
-
uint32_t mrmailbox_get_next_media(mrmailbox_t *mailbox, uint32_t curr_msg_id, int dir)
Returns all message IDs of the given types in a chat.
Definition: mrmailbox.c:1927
-
char * mrmailbox_get_version_str(void)
Find out the version of the Delta Chat core library.
Definition: mrmailbox.c:1490
-
void mrmailbox_disconnect(mrmailbox_t *mailbox)
Disonnect the mailbox from the server.
Definition: mrmailbox.c:1605
-
char * m_blobdir
full path of the blob directory in use.
Definition: mrmailbox.h:145
-
mrcontact_t * mrcontact_new()
Create a new contact object in memory.
Definition: mrcontact.c:32
-
carray * mrmailbox_get_fresh_msgs(mrmailbox_t *mailbox)
Returns the message IDs of all fresh messages of any chat.
Definition: mrmailbox.c:2052
-
mrmsg_t * mrmsg_new()
Create new message object.
Definition: mrmsg.c:41
-
uint32_t mrmailbox_send_msg(mrmailbox_t *mailbox, uint32_t chat_id, mrmsg_t *msg)
save message in database and send it, the given message object is not unref&#39;d by the function but som...
Definition: mrmailbox.c:3142
-
void mrmailbox_connect(mrmailbox_t *mailbox)
Connect to the mailbox using the configured settings.
Definition: mrmailbox.c:1578
-
int mrmailbox_get_total_msg_count(mrmailbox_t *mailbox, uint32_t chat_id)
Get the total number of messages in a chat.
Definition: mrmailbox.c:2588
-
carray * mrmailbox_get_chat_contacts(mrmailbox_t *mailbox, uint32_t chat_id)
Get contact IDs belonging to a chat.
Definition: mrmailbox.c:2003
-
void mrcontact_unref(mrcontact_t *ths)
Free a contact object.
Definition: mrcontact.c:49
-
void mrmailbox_unref(mrmailbox_t *mailbox)
Free a mailbox object.
Definition: mrmailbox.c:954
-
char * mrparam_get(mrparam_t *param, int key, const char *def)
Get value of a parameter.
Definition: mrparam.c:186
-
void mrmailbox_block_contact(mrmailbox_t *mailbox, uint32_t contact_id, int new_blocking)
Block or unblock a contact.
Definition: mrmailbox.c:4290
-
uintptr_t(* mrmailboxcb_t)(mrmailbox_t *, int event, uintptr_t data1, uintptr_t data2)
Callback function that should be given to mrmailbox_new().
Definition: mrmailbox.h:131
-
char * m_draft_text
NULL if unset.
Definition: mrchat.h:57
-
mrchatlist_t * mrmailbox_get_chatlist(mrmailbox_t *mailbox, int listflags, const char *query)
Get a list of chats.
Definition: mrmailbox.c:1663
-
mrmailbox_t * m_mailbox
!= NULL
Definition: mrchat.h:58
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
void mrparam_set(mrparam_t *param, int key, const char *value)
Set parameter to a string.
Definition: mrparam.c:253
-
void mrcontact_normalize_name(char *full_name)
Normalize a name in-place.
Definition: mrcontact.c:130
-
uint32_t mrmailbox_get_chat_id_by_contact_id(mrmailbox_t *mailbox, uint32_t contact_id)
Check, if there is a normal chat with a given contact.
Definition: mrmailbox.c:1789
-
int mrmailbox_get_fresh_msg_count(mrmailbox_t *mailbox, uint32_t chat_id)
Get the number of fresh messages in a chat.
Definition: mrmailbox.c:2616
-
void mrmailbox_delete_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Delete a chat:
Definition: mrmailbox.c:2773
-
void mrmailbox_set_draft(mrmailbox_t *mailbox, uint32_t chat_id, const char *msg)
Save a draft for a chat.
Definition: mrmailbox.c:2371
-
mrparam_t * m_param
!= NULL
Definition: mrchat.h:60
-
int mrmailbox_get_blocked_count(mrmailbox_t *mailbox)
Get the number of blocked contacts.
Definition: mrmailbox.c:4174
-
int mrmailbox_set_chat_image(mrmailbox_t *mailbox, uint32_t chat_id, const char *new_image)
Set group image.
Definition: mrmailbox.c:3476
-
void mrparam_set_int(mrparam_t *param, int key, int32_t value)
Set parameter to an integer.
Definition: mrparam.c:318
-
char * m_name
NULL if unset.
Definition: mrchat.h:55
-
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
-
void mrmsg_set_text(mrmsg_t *msg, const char *text)
Set the text of a message object.
Definition: mrmsg.c:125
-
carray * mrmailbox_get_known_contacts(mrmailbox_t *mailbox, const char *query)
Returns known and unblocked contacts.
Definition: mrmailbox.c:4076
-
char * mrmailbox_get_blobdir(mrmailbox_t *mailbox)
Get the blob directory.
Definition: mrmailbox.c:1276
-
mrmsg_t * mrmailbox_get_msg(mrmailbox_t *mailbox, uint32_t msg_id)
Get a single message object of the type mrmsg_t.
Definition: mrmailbox.c:4701
-
mrparam_t * m_param
MRP_FILE, MRP_WIDTH, MRP_HEIGHT etc.
Definition: mrmsg.h:73
-
char * m_text
message text or NULL if unset
Definition: mrmsg.h:72
-
void * m_userdata
the same pointer as given to mrmailbox_new(), may be used by the caller for any purpose ...
Definition: mrmailbox.h:143
-
void mrmsg_unref(mrmsg_t *msg)
Free an mrmsg_t object created eg.
Definition: mrmsg.c:66
-
char * m_authname
may be NULL or empty, this is the name authorized by the sender, only this name may be speaded to oth...
Definition: mrcontact.h:46
-
carray * mrmailbox_get_chat_media(mrmailbox_t *mailbox, uint32_t chat_id, int msg_type, int or_msg_type)
Returns all message IDs of the given types in a chat.
Definition: mrmailbox.c:1896
-
uint32_t m_id
Message ID.
Definition: mrmsg.h:45
-
uint32_t mrmailbox_create_chat_by_contact_id(mrmailbox_t *mailbox, uint32_t contact_id)
Create a normal chat with a single user.
Definition: mrmailbox.c:1816
-
void mrmailbox_forward_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt, uint32_t chat_id)
Forward a list of messages to another chat.
Definition: mrmailbox.c:4868
-
void mrmailbox_markseen_msgs(mrmailbox_t *mailbox, const uint32_t *msg_ids, int msg_cnt)
Mark a message as seen, updates the IMAP state and sends MDNs.
Definition: mrmailbox.c:5237
-
int m_state
Message state as one of the MR_MSG_STATE_* contstants.
Definition: mrmsg.h:70
-
time_t m_draft_timestamp
0 if there is no draft
Definition: mrchat.h:56
-
int m_blocked
Blocked state.
Definition: mrcontact.h:48
-
void mrmailbox_marknoticed_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Mark all message in a chat as noticed.
Definition: mrmailbox.c:1755
-
carray * mrmailbox_get_blocked_contacts(mrmailbox_t *mailbox)
Get blocked contacts.
Definition: mrmailbox.c:4140
-
mrcontact_t * mrmailbox_get_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Get a single contact object.
Definition: mrmailbox.c:4216
-
void mrchatlist_unref(mrchatlist_t *chatlist)
Free a mrchatlist_t object as created eg.
Definition: mrchatlist.c:62
-
void mrmailbox_marknoticed_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Mark all messages send by the given contact as noticed.
Definition: mrmailbox.c:4264
-
int mrmailbox_set_config_int(mrmailbox_t *ths, const char *key, int32_t value)
Configure the mailbox.
Definition: mrmailbox.c:1229
-
int32_t mrmailbox_get_config_int(mrmailbox_t *ths, const char *key, int32_t def)
Get a configuration option.
Definition: mrmailbox.c:1251
-
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
-
int mrmailbox_set_chat_name(mrmailbox_t *mailbox, uint32_t chat_id, const char *new_name)
Set group name.
Definition: mrmailbox.c:3399
-
char * mrmailbox_get_contact_encrinfo(mrmailbox_t *mailbox, uint32_t contact_id)
Get encryption info.
Definition: mrmailbox.c:4381
-
char * m_name
may be NULL or empty, this name should not be spreaded as it may be "Daddy" and so on; initially set ...
Definition: mrcontact.h:45
-
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
-
uint32_t m_id
The contact ID.
Definition: mrcontact.h:43
-
char * mrmailbox_get_config(mrmailbox_t *ths, const char *key, const char *def)
Get a configuration option.
Definition: mrmailbox.c:1207
-
char * mrmailbox_get_msg_info(mrmailbox_t *mailbox, uint32_t msg_id)
Get an informational text for a single message.
Definition: mrmailbox.c:4743
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
- - - - diff --git a/docs/user/html/mrmailbox__configure_8c_source.html b/docs/user/html/mrmailbox__configure_8c_source.html deleted file mode 100644 index 1405d9df..00000000 --- a/docs/user/html/mrmailbox__configure_8c_source.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmailbox_configure.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmailbox_configure.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <dirent.h>
24 #include "mrmailbox_internal.h"
25 #include "mrloginparam.h"
26 #include "mrimap.h"
27 #include "mrsmtp.h"
28 #include "mrosnative.h"
29 #include "mrsaxparser.h"
30 #include "mrjob.h"
31 
32 
33 /*******************************************************************************
34  * Tools
35  ******************************************************************************/
36 
37 
38 static char* read_autoconf_file(mrmailbox_t* mailbox, const char* url)
39 {
40  char* filecontent = NULL;
41  mrmailbox_log_info(mailbox, 0, "Testing %s ...", url);
42  filecontent = (char*)mailbox->m_cb(mailbox, MR_EVENT_HTTP_GET, (uintptr_t)url, 0);
43  if( filecontent == NULL ) {
44  mrmailbox_log_info(mailbox, 0, "Can't read file."); /* this is not a warning or an error, we're just testing */
45  return NULL;
46  }
47  return filecontent;
48 }
49 
50 
51 /*******************************************************************************
52  * Thunderbird's Autoconfigure
53  ******************************************************************************/
54 
55 
56 typedef struct moz_autoconfigure_t
57 {
58  const mrloginparam_t* m_in;
59  char* m_in_emaildomain;
60  char* m_in_emaillocalpart;
61 
62  mrloginparam_t* m_out;
63  int m_out_imap_set, m_out_smtp_set;
64 
65  /* currently, we assume there is only one emailProvider tag in the
66  file, see example at https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
67  moreover, we assume, the returned domains match the one queried. I've not seen another example (bp).
68  However, _if_ the assumpltions are wrong, we can add a first saxparser-pass that searches for the correct domain
69  and the second pass will look for the index found. */
70 
71  #define MOZ_SERVER_IMAP 1
72  #define MOZ_SERVER_SMTP 2
73  int m_tag_server;
74 
75  #define MOZ_HOSTNAME 10
76  #define MOZ_PORT 11
77  #define MOZ_USERNAME 12
78  #define MOZ_SOCKETTYPE 13
79  int m_tag_config;
80 
81 } moz_autoconfigure_t;
82 
83 
84 static void moz_autoconfigure_starttag_cb(void* userdata, const char* tag, char** attr)
85 {
86  moz_autoconfigure_t* moz_ac = (moz_autoconfigure_t*)userdata;
87  const char* p1;
88 
89  if( strcmp(tag, "incomingserver")==0 ) {
90  moz_ac->m_tag_server = (moz_ac->m_out_imap_set==0 && (p1=mrattr_find(attr, "type"))!=NULL && strcasecmp(p1, "imap")==0)? MOZ_SERVER_IMAP : 0;
91  moz_ac->m_tag_config = 0;
92  }
93  else if( strcmp(tag, "outgoingserver") == 0 ) {
94  moz_ac->m_tag_server = moz_ac->m_out_smtp_set==0? MOZ_SERVER_SMTP : 0;
95  moz_ac->m_tag_config = 0;
96  }
97  else if( strcmp(tag, "hostname") == 0 ) { moz_ac->m_tag_config = MOZ_HOSTNAME; }
98  else if( strcmp(tag, "port") == 0 ) { moz_ac->m_tag_config = MOZ_PORT; }
99  else if( strcmp(tag, "sockettype") == 0 ) { moz_ac->m_tag_config = MOZ_SOCKETTYPE; }
100  else if( strcmp(tag, "username") == 0 ) { moz_ac->m_tag_config = MOZ_USERNAME; }
101 }
102 
103 
104 static void moz_autoconfigure_text_cb(void* userdata, const char* text, int len)
105 {
106  moz_autoconfigure_t* moz_ac = (moz_autoconfigure_t*)userdata;
107 
108  char* val = safe_strdup(text);
109  mr_trim(val);
110  mr_str_replace(&val, "%EMAILADDRESS%", moz_ac->m_in->m_addr);
111  mr_str_replace(&val, "%EMAILLOCALPART%", moz_ac->m_in_emaillocalpart);
112  mr_str_replace(&val, "%EMAILDOMAIN%", moz_ac->m_in_emaildomain);
113 
114  if( moz_ac->m_tag_server == MOZ_SERVER_IMAP ) {
115  switch( moz_ac->m_tag_config ) {
116  case MOZ_HOSTNAME: free(moz_ac->m_out->m_mail_server); moz_ac->m_out->m_mail_server = val; val = NULL; break;
117  case MOZ_PORT: moz_ac->m_out->m_mail_port = atoi(val); break;
118  case MOZ_USERNAME: free(moz_ac->m_out->m_mail_user); moz_ac->m_out->m_mail_user = val; val = NULL; break;
119  case MOZ_SOCKETTYPE:
120  if( strcasecmp(val, "ssl")==0 ) { moz_ac->m_out->m_server_flags |=MR_IMAP_SOCKET_SSL; }
121  if( strcasecmp(val, "starttls")==0 ) { moz_ac->m_out->m_server_flags |=MR_IMAP_SOCKET_STARTTLS; }
122  if( strcasecmp(val, "plain")==0 ) { moz_ac->m_out->m_server_flags |=MR_IMAP_SOCKET_PLAIN; }
123  break;
124  }
125  }
126  else if( moz_ac->m_tag_server == MOZ_SERVER_SMTP ) {
127  switch( moz_ac->m_tag_config ) {
128  case MOZ_HOSTNAME: free(moz_ac->m_out->m_send_server); moz_ac->m_out->m_send_server = val; val = NULL; break;
129  case MOZ_PORT: moz_ac->m_out->m_send_port = atoi(val); break;
130  case MOZ_USERNAME: free(moz_ac->m_out->m_send_user); moz_ac->m_out->m_send_user = val; val = NULL; break;
131  case MOZ_SOCKETTYPE:
132  if( strcasecmp(val, "ssl")==0 ) { moz_ac->m_out->m_server_flags |=MR_SMTP_SOCKET_SSL; }
133  if( strcasecmp(val, "starttls")==0 ) { moz_ac->m_out->m_server_flags |=MR_SMTP_SOCKET_STARTTLS; }
134  if( strcasecmp(val, "plain")==0 ) { moz_ac->m_out->m_server_flags |=MR_SMTP_SOCKET_PLAIN; }
135  break;
136  }
137  }
138 
139  free(val);
140 }
141 
142 
143 static void moz_autoconfigure_endtag_cb(void* userdata, const char* tag)
144 {
145  moz_autoconfigure_t* moz_ac = (moz_autoconfigure_t*)userdata;
146 
147  if( strcmp(tag, "incomingserver")==0 ) {
148  moz_ac->m_tag_server = 0;
149  moz_ac->m_tag_config = 0;
150  moz_ac->m_out_imap_set = 1;
151  }
152  else if( strcmp(tag, "outgoingserver")==0 ) {
153  moz_ac->m_tag_server = 0;
154  moz_ac->m_tag_config = 0;
155  moz_ac->m_out_smtp_set = 1;
156  }
157  else {
158  moz_ac->m_tag_config = 0;
159  }
160 }
161 
162 
163 static mrloginparam_t* moz_autoconfigure(mrmailbox_t* mailbox, const char* url, const mrloginparam_t* param_in)
164 {
165  char* xml_raw = NULL;
166  moz_autoconfigure_t moz_ac;
167 
168  memset(&moz_ac, 0, sizeof(moz_autoconfigure_t));
169 
170  if( (xml_raw=read_autoconf_file(mailbox, url))==NULL ) {
171  goto cleanup;
172  }
173 
174  moz_ac.m_in = param_in;
175  moz_ac.m_in_emaillocalpart = safe_strdup(param_in->m_addr); char* p = strchr(moz_ac.m_in_emaillocalpart, '@'); if( p == NULL ) { goto cleanup; } *p = 0;
176  moz_ac.m_in_emaildomain = safe_strdup(p+1);
177  moz_ac.m_out = mrloginparam_new();
178 
179  mrsaxparser_t saxparser;
180  mrsaxparser_init (&saxparser, &moz_ac);
181  mrsaxparser_set_tag_handler (&saxparser, moz_autoconfigure_starttag_cb, moz_autoconfigure_endtag_cb);
182  mrsaxparser_set_text_handler(&saxparser, moz_autoconfigure_text_cb);
183  mrsaxparser_parse (&saxparser, xml_raw);
184 
185  if( moz_ac.m_out->m_mail_server == NULL
186  || moz_ac.m_out->m_mail_port == 0
187  || moz_ac.m_out->m_send_server == NULL
188  || moz_ac.m_out->m_send_port == 0 )
189  {
190  { char* r = mrloginparam_get_readable(moz_ac.m_out); mrmailbox_log_warning(mailbox, 0, "Bad or incomplete autoconfig: %s", r); free(r); }
191 
192  mrloginparam_unref(moz_ac.m_out); /* autoconfig failed for the given URL */
193  moz_ac.m_out = NULL;
194  goto cleanup;
195  }
196 
197 cleanup:
198  free(xml_raw);
199  free(moz_ac.m_in_emaildomain);
200  free(moz_ac.m_in_emaillocalpart);
201  return moz_ac.m_out; /* may be NULL */
202 }
203 
204 
205 /*******************************************************************************
206  * Outlook's Autodiscover
207  ******************************************************************************/
208 
209 
210 typedef struct outlk_autodiscover_t
211 {
212  const mrloginparam_t* m_in;
213 
214  mrloginparam_t* m_out;
215  int m_out_imap_set, m_out_smtp_set;
216 
217  /* file format: https://msdn.microsoft.com/en-us/library/bb204278(v=exchg.80).aspx */
218  #define OUTLK_TYPE 1
219  #define OUTLK_SERVER 2
220  #define OUTLK_PORT 3
221  #define OUTLK_SSL 4
222  #define OUTLK_REDIRECTURL 5
223  #define _OUTLK_COUNT_ 6
224  int m_tag_config;
225 
226  char* m_config[_OUTLK_COUNT_];
227  char* m_redirect;
228 
229 } outlk_autodiscover_t;
230 
231 
232 static void outlk_clean_config(outlk_autodiscover_t* outlk_ad)
233 {
234  int i;
235  for( i = 0; i < _OUTLK_COUNT_; i++ ) {
236  free(outlk_ad->m_config[i]);
237  outlk_ad->m_config[i] = NULL;
238  }
239 }
240 
241 
242 static void outlk_autodiscover_starttag_cb(void* userdata, const char* tag, char** attr)
243 {
244  outlk_autodiscover_t* outlk_ad = (outlk_autodiscover_t*)userdata;
245 
246  if( strcmp(tag, "protocol") == 0 ) { outlk_clean_config(outlk_ad); } /* this also cleans "redirecturl", however, this is not problem as the protocol block is only valid for action "settings". */
247  else if( strcmp(tag, "type") == 0 ) { outlk_ad->m_tag_config = OUTLK_TYPE; }
248  else if( strcmp(tag, "server") == 0 ) { outlk_ad->m_tag_config = OUTLK_SERVER; }
249  else if( strcmp(tag, "port") == 0 ) { outlk_ad->m_tag_config = OUTLK_PORT; }
250  else if( strcmp(tag, "ssl") == 0 ) { outlk_ad->m_tag_config = OUTLK_SSL; }
251  else if( strcmp(tag, "redirecturl") == 0 ) { outlk_ad->m_tag_config = OUTLK_REDIRECTURL; }
252 }
253 
254 
255 static void outlk_autodiscover_text_cb(void* userdata, const char* text, int len)
256 {
257  outlk_autodiscover_t* outlk_ad = (outlk_autodiscover_t*)userdata;
258 
259  char* val = safe_strdup(text);
260  mr_trim(val);
261 
262  free(outlk_ad->m_config[outlk_ad->m_tag_config]);
263  outlk_ad->m_config[outlk_ad->m_tag_config] = val;
264 }
265 
266 
267 static void outlk_autodiscover_endtag_cb(void* userdata, const char* tag)
268 {
269  outlk_autodiscover_t* outlk_ad = (outlk_autodiscover_t*)userdata;
270 
271  if( strcmp(tag, "protocol")==0 )
272  {
273  /* copy collected confituration to m_out (we have to delay this as we do not know when the <type> tag appears in the sax-stream) */
274  if( outlk_ad->m_config[OUTLK_TYPE] )
275  {
276  int port = atoi_null_is_0(outlk_ad->m_config[OUTLK_PORT]),
277  ssl_on = (outlk_ad->m_config[OUTLK_SSL] && strcasecmp(outlk_ad->m_config[OUTLK_SSL], "on" )==0),
278  ssl_off = (outlk_ad->m_config[OUTLK_SSL] && strcasecmp(outlk_ad->m_config[OUTLK_SSL], "off")==0);
279 
280  if( strcasecmp(outlk_ad->m_config[OUTLK_TYPE], "imap")==0 && outlk_ad->m_out_imap_set==0 ) {
281  outlk_ad->m_out->m_mail_server = strdup_keep_null(outlk_ad->m_config[OUTLK_SERVER]);
282  outlk_ad->m_out->m_mail_port = port;
283  if( ssl_on ) { outlk_ad->m_out->m_server_flags |= MR_IMAP_SOCKET_SSL; }
284  else if( ssl_off ) { outlk_ad->m_out->m_server_flags |= MR_IMAP_SOCKET_PLAIN; }
285  outlk_ad->m_out_imap_set = 1;
286  }
287  else if( strcasecmp(outlk_ad->m_config[OUTLK_TYPE], "smtp")==0 && outlk_ad->m_out_smtp_set==0 ) {
288  outlk_ad->m_out->m_send_server = strdup_keep_null(outlk_ad->m_config[OUTLK_SERVER]);
289  outlk_ad->m_out->m_send_port = port;
290  if( ssl_on ) { outlk_ad->m_out->m_server_flags |= MR_SMTP_SOCKET_SSL; }
291  else if( ssl_off ) { outlk_ad->m_out->m_server_flags |= MR_SMTP_SOCKET_PLAIN; }
292  outlk_ad->m_out_smtp_set = 1;
293  }
294  }
295 
296  outlk_clean_config(outlk_ad);
297  }
298  outlk_ad->m_tag_config = 0;
299 }
300 
301 
302 static mrloginparam_t* outlk_autodiscover(mrmailbox_t* mailbox, const char* url__, const mrloginparam_t* param_in)
303 {
304  char* xml_raw = NULL, *url = safe_strdup(url__);
305  outlk_autodiscover_t outlk_ad;
306  int i;
307 
308  for( i = 0; i < 10 /* follow up to 10 xml-redirects (http-redirects are followed in read_autoconf_file() */; i++ )
309  {
310  memset(&outlk_ad, 0, sizeof(outlk_autodiscover_t));
311 
312  if( (xml_raw=read_autoconf_file(mailbox, url))==NULL ) {
313  goto cleanup;
314  }
315 
316  outlk_ad.m_in = param_in;
317  outlk_ad.m_out = mrloginparam_new();
318 
319  mrsaxparser_t saxparser;
320  mrsaxparser_init (&saxparser, &outlk_ad);
321  mrsaxparser_set_tag_handler (&saxparser, outlk_autodiscover_starttag_cb, outlk_autodiscover_endtag_cb);
322  mrsaxparser_set_text_handler(&saxparser, outlk_autodiscover_text_cb);
323  mrsaxparser_parse (&saxparser, xml_raw);
324 
325  if( outlk_ad.m_config[OUTLK_REDIRECTURL] && outlk_ad.m_config[OUTLK_REDIRECTURL][0] ) {
326  free(url);
327  url = safe_strdup(outlk_ad.m_config[OUTLK_REDIRECTURL]);
328  mrloginparam_unref(outlk_ad.m_out);
329  outlk_clean_config(&outlk_ad);
330  free(xml_raw); xml_raw = NULL;
331  }
332  else {
333  break;
334  }
335  }
336 
337  if( outlk_ad.m_out->m_mail_server == NULL
338  || outlk_ad.m_out->m_mail_port == 0
339  || outlk_ad.m_out->m_send_server == NULL
340  || outlk_ad.m_out->m_send_port == 0 )
341  {
342  { char* r = mrloginparam_get_readable(outlk_ad.m_out); mrmailbox_log_warning(mailbox, 0, "Bad or incomplete autoconfig: %s", r); free(r); }
343  mrloginparam_unref(outlk_ad.m_out); /* autoconfig failed for the given URL */
344  outlk_ad.m_out = NULL;
345  goto cleanup;
346  }
347 
348 cleanup:
349  free(url);
350  free(xml_raw);
351  outlk_clean_config(&outlk_ad);
352  return outlk_ad.m_out; /* may be NULL */
353 }
354 
355 
356 /*******************************************************************************
357  * The configuration thread
358  ******************************************************************************/
359 
360 
361 static pthread_t s_configure_thread;
362 static int s_configure_thread_created = 0;
363 static int s_configure_do_exit = 1; /* the value 1 avoids mrmailbox_configure_cancel() from stopping already stopped threads */
364 
365 
366 static void* configure_thread_entry_point(void* entry_arg)
367 {
368  mrmailbox_t* mailbox = (mrmailbox_t*)entry_arg;
369  mrosnative_setup_thread(mailbox); /* must be very first */
370 
371  int success = 0, locked = 0, i;
372  int imap_connected = 0;
373 
374  mrloginparam_t* param = mrloginparam_new();
375  char* param_domain = NULL; /* just a pointer inside param, must not be freed! */
376  char* param_addr_urlencoded = NULL;
377  mrloginparam_t* param_autoconfig = NULL;
378 
379  #define PROGRESS(p) \
380  if( s_configure_do_exit ) { goto exit_; } \
381  mailbox->m_cb(mailbox, MR_EVENT_CONFIGURE_PROGRESS, (p), 0);
382 
383  mrmailbox_log_info(mailbox, 0, "Configure ...");
384 
385  PROGRESS(0)
386 
387  if( mailbox->m_cb(mailbox, MR_EVENT_IS_ONLINE, 0, 0)!=1 ) {
388  mrmailbox_log_error(mailbox, MR_ERR_NONETWORK, NULL);
389  goto exit_;
390  }
391 
392  PROGRESS(10)
393 
394  /* 1. Load the parameters and check email-address and password
395  **************************************************************************/
396 
397  mrsqlite3_lock(mailbox->m_sql);
398  locked = 1;
399 
400  mrloginparam_read__(param, mailbox->m_sql, "");
401 
402  mrsqlite3_unlock(mailbox->m_sql);
403  locked = 0;
404 
405  if( param->m_addr == NULL ) {
406  mrmailbox_log_error(mailbox, 0, "Please enter the email address.");
407  goto exit_;
408  }
409  mr_trim(param->m_addr);
410 
411  param_domain = strchr(param->m_addr, '@');
412  if( param_domain==NULL || param_domain[0]==0 ) {
413  mrmailbox_log_error(mailbox, 0, "Bad email-address.");
414  goto exit_;
415  }
416  param_domain++;
417 
418  param_addr_urlencoded = mr_url_encode(param->m_addr);
419 
420  /* if no password is given, assume an empty password.
421  (in general, unset values are NULL, not the empty string, this allows to use eg. empty user names or empty passwords) */
422  if( param->m_mail_pw == NULL ) {
423  param->m_mail_pw = safe_strdup(NULL);
424  }
425 
426  PROGRESS(20)
427 
428 
429  /* 2. Autoconfig
430  **************************************************************************/
431 
432  if( param->m_mail_server == NULL
433  && param->m_mail_port == 0
434  /*&&param->m_mail_user == NULL -- the user can enter a loginname which is used by autoconfig then */
435  && param->m_send_server == NULL
436  && param->m_send_port == 0
437  && param->m_send_user == NULL
438  /*&&param->m_send_pw == NULL -- the password cannot be auto-configured and is no criterion for autoconfig or not */
439  && param->m_server_flags == 0 )
440  {
441  /* A. Search configurations from the domain used in the email-address */
442  for( i = 0; i <= 1; i++ ) {
443  if( param_autoconfig==NULL ) {
444  char* url = mr_mprintf("%s://autoconfig.%s/mail/config-v1.1.xml?emailaddress=%s", i==0?"http":"https", param_domain, param_addr_urlencoded); /* Thunderbird may or may not use SSL */
445  param_autoconfig = moz_autoconfigure(mailbox, url, param);
446  free(url);
447  PROGRESS(30+i*5)
448  }
449  }
450 
451  for( i = 0; i <= 1; i++ ) {
452  if( param_autoconfig==NULL ) {
453  char* url = mr_mprintf("https://%s%s/autodiscover/autodiscover.xml", i==0?"":"autodiscover.", param_domain); /* Outlook uses always SSL but different domains */
454  param_autoconfig = outlk_autodiscover(mailbox, url, param);
455  free(url);
456  PROGRESS(40+i*5)
457  }
458  }
459 
460  /* B. If we have no configuration yet, search configuration in Thunderbird's centeral database */
461  if( param_autoconfig==NULL )
462  {
463  char* url = mr_mprintf("https://autoconfig.thunderbird.net/v1.1/%s", param_domain); /* always SSL for Thunderbird's database */
464  param_autoconfig = moz_autoconfigure(mailbox, url, param);
465  free(url);
466  PROGRESS(50)
467  }
468 
469  /* C. Do we have any result? */
470  if( param_autoconfig )
471  {
472  { char* r = mrloginparam_get_readable(param_autoconfig); mrmailbox_log_info(mailbox, 0, "Got autoconfig: %s", r); free(r); }
473 
474  if( param_autoconfig->m_mail_user ) {
475  free(param->m_mail_user);
476  param->m_mail_user= strdup_keep_null(param_autoconfig->m_mail_user);
477  }
478  param->m_mail_server = strdup_keep_null(param_autoconfig->m_mail_server); /* all other values are always NULL when entering autoconfig */
479  param->m_mail_port = param_autoconfig->m_mail_port;
480  param->m_send_server = strdup_keep_null(param_autoconfig->m_send_server);
481  param->m_send_port = param_autoconfig->m_send_port;
482  param->m_send_user = strdup_keep_null(param_autoconfig->m_send_user);
483  param->m_server_flags = param_autoconfig->m_server_flags;
484 
485  /* althoug param_autoconfig's data are no longer needed from, it is important to keep the object as
486  we may enter "deep guessing" if we could not read a configuration */
487  }
488  }
489 
490 
491  /* 3. Internal specials (eg. for uploading to chats-folder etc.)
492  **************************************************************************/
493 
494  if( strcasecmp(param_domain, "gmail.com")==0 || strcasecmp(param_domain, "googlemail.com")==0 )
495  {
496  /* NB: Checking GMa'l too often (<10 Minutes) may result in blocking, says https://github.com/itprojects/InboxPager/blob/HEAD/README.md#gmail-configuration
497  Also note https://www.google.com/settings/security/lesssecureapps */
498  param->m_server_flags |= MR_AUTH_XOAUTH2 | MR_NO_EXTRA_IMAP_UPLOAD | MR_NO_MOVE_TO_CHATS;
499  }
500 
501 
502  /* 2. Fill missing fields with defaults
503  **************************************************************************/
504 
505  #define TYPICAL_IMAP_SSL_PORT 993 /* our default */
506  #define TYPICAL_IMAP_STARTTLS_PORT 143 /* not used very often but eg. by posteo.de, default for PLAIN */
507 
508  #define TYPICAL_SMTP_SSL_PORT 465 /* our default */
509  #define TYPICAL_SMTP_STARTTLS_PORT 587 /* also used very often, SSL:STARTTLS is maybe 50:50 */
510  #define TYPICAL_SMTP_PLAIN_PORT 25
511 
512  if( param->m_mail_server == NULL ) {
513  param->m_mail_server = mr_mprintf("imap.%s", param_domain);
514  }
515 
516  if( param->m_mail_port == 0 ) {
517  param->m_mail_port = (param->m_server_flags&(MR_IMAP_SOCKET_STARTTLS|MR_IMAP_SOCKET_PLAIN))? TYPICAL_IMAP_STARTTLS_PORT : TYPICAL_IMAP_SSL_PORT;
518  }
519 
520  if( param->m_mail_user == NULL ) {
521  param->m_mail_user = safe_strdup(param->m_addr);
522  }
523 
524  if( param->m_send_server == NULL && param->m_mail_server ) {
525  param->m_send_server = safe_strdup(param->m_mail_server);
526  if( strncmp(param->m_send_server, "imap.", 5)==0 ) {
527  memcpy(param->m_send_server, "smtp", 4);
528  }
529  }
530 
531  if( param->m_send_port == 0 ) {
532  param->m_send_port = (param->m_server_flags&MR_SMTP_SOCKET_STARTTLS)? TYPICAL_SMTP_STARTTLS_PORT :
533  ((param->m_server_flags&MR_SMTP_SOCKET_PLAIN)? TYPICAL_SMTP_PLAIN_PORT : TYPICAL_SMTP_SSL_PORT);
534  }
535 
536  if( param->m_send_user == NULL && param->m_mail_user ) {
537  param->m_send_user = safe_strdup(param->m_mail_user);
538  }
539 
540  if( param->m_send_pw == NULL && param->m_mail_pw ) {
541  param->m_send_pw = safe_strdup(param->m_mail_pw);
542  }
543 
544  if( !mr_exactly_one_bit_set(param->m_server_flags&MR_AUTH_FLAGS) )
545  {
546  param->m_server_flags &= ~MR_AUTH_FLAGS;
547  param->m_server_flags |= MR_AUTH_NORMAL;
548  }
549 
550  if( !mr_exactly_one_bit_set(param->m_server_flags&MR_IMAP_SOCKET_FLAGS) )
551  {
552  param->m_server_flags &= ~MR_IMAP_SOCKET_FLAGS;
553  param->m_server_flags |= (param->m_send_port==TYPICAL_IMAP_STARTTLS_PORT? MR_IMAP_SOCKET_STARTTLS : MR_IMAP_SOCKET_SSL);
554  }
555 
556  if( !mr_exactly_one_bit_set(param->m_server_flags&MR_SMTP_SOCKET_FLAGS) )
557  {
558  param->m_server_flags &= ~MR_SMTP_SOCKET_FLAGS;
559  param->m_server_flags |= ( param->m_send_port==TYPICAL_SMTP_STARTTLS_PORT? MR_SMTP_SOCKET_STARTTLS :
560  (param->m_send_port==TYPICAL_SMTP_PLAIN_PORT? MR_SMTP_SOCKET_PLAIN: MR_SMTP_SOCKET_SSL) );
561  }
562 
563 
564  /* do we have a complete configuration? */
565  if( param->m_addr == NULL
566  || param->m_mail_server == NULL
567  || param->m_mail_port == 0
568  || param->m_mail_user == NULL
569  || param->m_mail_pw == NULL
570  || param->m_send_server == NULL
571  || param->m_send_port == 0
572  || param->m_send_user == NULL
573  || param->m_send_pw == NULL
574  || param->m_server_flags == 0 )
575  {
576  mrmailbox_log_error(mailbox, 0, "Account settings incomplete.");
577  goto exit_;
578  }
579 
580  PROGRESS(60)
581 
582  /* try to connect to IMAP */
583  { char* r = mrloginparam_get_readable(param); mrmailbox_log_info(mailbox, 0, "Trying: %s", r); free(r); }
584 
585  if( !mrimap_connect(mailbox->m_imap, param) ) {
586  goto exit_;
587  }
588  imap_connected = 1;
589 
590  PROGRESS(80)
591 
592  /* try to connect to SMTP - if we did not got an autoconfig, the first try was SSL-465 and we do a second try with STARTTLS-587 */
593  if( !mrsmtp_connect(mailbox->m_smtp, param) ) {
594  if( param_autoconfig ) {
595  goto exit_;
596  }
597 
598  PROGRESS(85)
599 
600  param->m_server_flags &= ~MR_SMTP_SOCKET_FLAGS;
601  param->m_server_flags |= MR_SMTP_SOCKET_STARTTLS;
602  param->m_send_port = TYPICAL_SMTP_STARTTLS_PORT;
603  { char* r = mrloginparam_get_readable(param); mrmailbox_log_info(mailbox, 0, "Trying: %s", r); free(r); }
604 
605  if( !mrsmtp_connect(mailbox->m_smtp, param) ) {
606  goto exit_;
607  }
608  }
609 
610  PROGRESS(90)
611 
612  /* configuration success - write back the configured parameters with the "configured_" prefix; also write the "configured"-flag */
613  mrsqlite3_lock(mailbox->m_sql);
614  locked = 1;
615 
616  mrloginparam_write__(param, mailbox->m_sql, "configured_" /*the trailing underscore is correct*/);
617  mrsqlite3_set_config_int__(mailbox->m_sql, "configured", 1);
618 
619  mrsqlite3_unlock(mailbox->m_sql);
620  locked = 0;
621 
622  success = 1;
623  mrmailbox_log_info(mailbox, 0, "Configure completed successfully.");
624 
625 exit_:
626  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
627  if( !success && imap_connected ) {
628  mrimap_disconnect(mailbox->m_imap);
629  }
630  mrloginparam_unref(param);
631  mrloginparam_unref(param_autoconfig);
632  free(param_addr_urlencoded);
633 
634  s_configure_do_exit = 1; /* set this before sending MR_EVENT_CONFIGURE_ENDED, avoids mrmailbox_configure_cancel() to stop the thread */
635  mailbox->m_cb(mailbox, MR_EVENT_CONFIGURE_ENDED, success, 0);
636  s_configure_thread_created = 0;
637  mrosnative_unsetup_thread(mailbox); /* must be very last */
638  return NULL;
639 }
640 
641 
642 /*******************************************************************************
643  * Main interface
644  ******************************************************************************/
645 
646 
666 {
667  if( mailbox == NULL ) {
668  return;
669  }
670 
671  if( !mrsqlite3_is_open(mailbox->m_sql) ) {
672  mrmailbox_log_error(mailbox, 0, "Cannot configure, database not opened.");
673  s_configure_do_exit = 1;
674  mailbox->m_cb(mailbox, MR_EVENT_CONFIGURE_ENDED, 0, 0);
675  return;
676  }
677 
678  if( s_configure_thread_created || s_configure_do_exit == 0 ) {
679  mrmailbox_log_error(mailbox, 0, "Already configuring.");
680  return; /* do not send a MR_EVENT_CONFIGURE_ENDED event, this is done by the already existing thread */
681  }
682 
683  s_configure_thread_created = 1;
684  s_configure_do_exit = 0;
685 
686  /* disconnect */
687  mrmailbox_disconnect(mailbox);
688  mrsqlite3_lock(mailbox->m_sql);
689  //mrsqlite3_set_config_int__(mailbox->m_sql, "configured", 0); -- NO: we do _not_ reset this flag if it was set once; otherwise the user won't get back to his chats (as an alternative, we could change the UI). Moreover, and not changeable in the UI, we use this flag to check if we shall search for backups.
690  mailbox->m_smtp->m_log_connect_errors = 1;
691  mailbox->m_imap->m_log_connect_errors = 1;
692  mrjob_kill_action__(mailbox, MRJ_CONNECT_TO_IMAP);
693  mrsqlite3_unlock(mailbox->m_sql);
694 
695  /* start a thread for the configuration it self, when done, we'll post a MR_EVENT_CONFIGURE_ENDED event */
696  pthread_create(&s_configure_thread, NULL, configure_thread_entry_point, mailbox);
697 }
698 
699 
710 {
711  if( mailbox == NULL ) {
712  return;
713  }
714 
715  if( s_configure_thread_created && s_configure_do_exit==0 )
716  {
717  mrmailbox_log_info(mailbox, 0, "Stopping configure-thread...");
718  s_configure_do_exit = 1;
719  pthread_join(s_configure_thread, NULL);
720  mrmailbox_log_info(mailbox, 0, "Configure-thread stopped.");
721  }
722 }
723 
724 
736 {
737  int is_configured;
738 
739  if( mailbox == NULL ) {
740  return 0;
741  }
742 
743  if( mrimap_is_connected(mailbox->m_imap) ) { /* if we're connected, we're also configured. this check will speed up the check as no database is involved */
744  return 1;
745  }
746 
747  mrsqlite3_lock(mailbox->m_sql);
748 
749  is_configured = mrsqlite3_get_config_int__(mailbox->m_sql, "configured", 0);
750 
751  mrsqlite3_unlock(mailbox->m_sql);
752 
753  return is_configured? 1 : 0;
754 }
755 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrmailbox_configure_and_connect(mrmailbox_t *mailbox)
Configure and connect a mailbox.
-
int mrmailbox_is_configured(mrmailbox_t *mailbox)
Check if the mailbox is already configured.
-
void mrmailbox_configure_cancel(mrmailbox_t *mailbox)
Cancel an configuration started by mrmailbox_configure_and_connect().
-
- - - - diff --git a/docs/user/html/mrmailbox__e2ee_8c_source.html b/docs/user/html/mrmailbox__e2ee_8c_source.html deleted file mode 100644 index bbc72eb5..00000000 --- a/docs/user/html/mrmailbox__e2ee_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmailbox_e2ee.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmailbox_e2ee.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrpgp.h"
25 #include "mrapeerstate.h"
26 #include "mraheader.h"
27 #include "mrkeyring.h"
28 #include "mrmimeparser.h"
29 
30 
31 static struct mailmime* new_data_part(void* data, size_t data_bytes, char* default_content_type, int default_encoding)
32 {
33  //char basename_buf[PATH_MAX];
34  struct mailmime_mechanism * encoding;
35  struct mailmime_content * content;
36  struct mailmime * mime;
37  //int r;
38  //char * dup_filename;
39  struct mailmime_fields * mime_fields;
40  int encoding_type;
41  char * content_type_str;
42  int do_encoding;
43 
44  /*if (filename != NULL) {
45  strncpy(basename_buf, filename, PATH_MAX);
46  libetpan_basename(basename_buf);
47  }*/
48 
49  encoding = NULL;
50 
51  /* default content-type */
52  if (default_content_type == NULL)
53  content_type_str = "application/octet-stream";
54  else
55  content_type_str = default_content_type;
56 
57  content = mailmime_content_new_with_str(content_type_str);
58  if (content == NULL) {
59  goto free_content;
60  }
61 
62  do_encoding = 1;
63  if (content->ct_type->tp_type == MAILMIME_TYPE_COMPOSITE_TYPE) {
64  struct mailmime_composite_type * composite;
65 
66  composite = content->ct_type->tp_data.tp_composite_type;
67 
68  switch (composite->ct_type) {
69  case MAILMIME_COMPOSITE_TYPE_MESSAGE:
70  if (strcasecmp(content->ct_subtype, "rfc822") == 0)
71  do_encoding = 0;
72  break;
73 
74  case MAILMIME_COMPOSITE_TYPE_MULTIPART:
75  do_encoding = 0;
76  break;
77  }
78  }
79 
80  if (do_encoding) {
81  if (default_encoding == -1)
82  encoding_type = MAILMIME_MECHANISM_BASE64;
83  else
84  encoding_type = default_encoding;
85 
86  /* default Content-Transfer-Encoding */
87  encoding = mailmime_mechanism_new(encoding_type, NULL);
88  if (encoding == NULL) {
89  goto free_content;
90  }
91  }
92 
93  mime_fields = mailmime_fields_new_with_data(encoding,
94  NULL, NULL, NULL, NULL);
95  if (mime_fields == NULL) {
96  goto free_content;
97  }
98 
99  mime = mailmime_new_empty(content, mime_fields);
100  if (mime == NULL) {
101  goto free_mime_fields;
102  }
103 
104  /*if ((filename != NULL) && (mime->mm_type == MAILMIME_SINGLE)) {
105  // duplicates the file so that the file can be deleted when
106  // the MIME part is done
107  dup_filename = dup_file(privacy, filename);
108  if (dup_filename == NULL) {
109  goto free_mime;
110  }
111 
112  r = mailmime_set_body_file(mime, dup_filename);
113  if (r != MAILIMF_NO_ERROR) {
114  free(dup_filename);
115  goto free_mime;
116  }
117  }*/
118  if( data!=NULL && data_bytes>0 && mime->mm_type == MAILMIME_SINGLE ) {
119  mailmime_set_body_text(mime, data, data_bytes);
120  }
121 
122  return mime;
123 
124 // free_mime:
125  //mailmime_free(mime);
126  goto err;
127  free_mime_fields:
128  mailmime_fields_free(mime_fields);
129  mailmime_content_free(content);
130  goto err;
131  free_content:
132  if (encoding != NULL)
133  mailmime_mechanism_free(encoding);
134  if (content != NULL)
135  mailmime_content_free(content);
136  err:
137  return NULL;
138 }
139 
140 
141 /*******************************************************************************
142  * Tools
143  ******************************************************************************/
144 
145 
146 static int load_or_generate_self_public_key__(mrmailbox_t* mailbox, mrkey_t* public_key, const char* self_addr,
147  struct mailmime* random_data_mime /*for an extra-seed of the random generator. For speed reasons, only give _available_ pointers here, do not create any data - in very most cases, the key is not generated!*/)
148 {
149  static int s_in_key_creation = 0; /* avoid double creation (we unlock the database during creation) */
150  int key_created = 0;
151  int success = 0, key_creation_here = 0;
152 
153  if( mailbox == NULL || public_key == NULL ) {
154  goto cleanup;
155  }
156 
157  if( !mrkey_load_self_public__(public_key, self_addr, mailbox->m_sql) )
158  {
159  /* create the keypair - this may take a moment, however, as this is in a thread, this is no big deal */
160  if( s_in_key_creation ) { goto cleanup; }
161  key_creation_here = 1;
162  s_in_key_creation = 1;
163 
164  /* seed the random generator */
165  {
166  uintptr_t seed[4];
167  seed[0] = (uintptr_t)time(NULL); /* time */
168  seed[1] = (uintptr_t)seed; /* stack */
169  seed[2] = (uintptr_t)public_key; /* heap */
170  seed[3] = (uintptr_t)pthread_self(); /* thread ID */
171  mrpgp_rand_seed(mailbox, seed, sizeof(seed));
172 
173  if( random_data_mime ) {
174  MMAPString* random_data_mmap = NULL;
175  int col = 0;
176  if( (random_data_mmap=mmap_string_new(""))==NULL ) {
177  goto cleanup;
178  }
179  mailmime_write_mem(random_data_mmap, &col, random_data_mime);
180  mrpgp_rand_seed(mailbox, random_data_mmap->str, random_data_mmap->len);
181  mmap_string_free(random_data_mmap);
182  }
183  }
184 
185  {
186  mrkey_t* private_key = mrkey_new();
187 
188  mrmailbox_log_info(mailbox, 0, "Generating keypair ...");
189 
190  mrsqlite3_unlock(mailbox->m_sql); /* SIC! unlock database during creation - otherwise the GUI may hang */
191 
192  /* The public key must contain the following:
193  - a signing-capable primary key Kp
194  - a user id
195  - a self signature
196  - an encryption-capable subkey Ke
197  - a binding signature over Ke by Kp
198  (see https://autocrypt.readthedocs.io/en/latest/level0.html#type-p-openpgp-based-key-data )*/
199  key_created = mrpgp_create_keypair(mailbox, self_addr, public_key, private_key);
200 
201  mrsqlite3_lock(mailbox->m_sql);
202 
203  if( !key_created ) {
204  mrmailbox_log_warning(mailbox, 0, "Cannot create keypair.");
205  goto cleanup;
206  }
207 
208  if( !mrpgp_is_valid_key(mailbox, public_key)
209  || !mrpgp_is_valid_key(mailbox, private_key) ) {
210  mrmailbox_log_warning(mailbox, 0, "Generated keys are not valid.");
211  goto cleanup;
212  }
213 
214  if( !mrkey_save_self_keypair__(public_key, private_key, self_addr, 1/*set default*/, mailbox->m_sql) ) {
215  mrmailbox_log_warning(mailbox, 0, "Cannot save keypair.");
216  goto cleanup;
217  }
218 
219  mrmailbox_log_info(mailbox, 0, "Keypair generated.");
220 
221  mrkey_unref(private_key);
222  }
223  }
224 
225  success = 1;
226 
227 cleanup:
228  if( key_creation_here ) { s_in_key_creation = 0; }
229  return success;
230 }
231 
232 
233 int mrmailbox_ensure_secret_key_exists(mrmailbox_t* mailbox)
234 {
235  /* normally, the key is generated as soon as the first mail is send
236  (this is to gain some extra-random-seed by the message content and the timespan between program start and message sending) */
237  int success = 0, locked = 0;
238  mrkey_t* public_key = mrkey_new();
239  char* self_addr = NULL;
240 
241  if( mailbox==NULL || public_key==NULL ) {
242  goto cleanup;
243  }
244 
245  mrsqlite3_lock(mailbox->m_sql);
246  locked = 1;
247 
248  if( (self_addr=mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL))==NULL ) {
249  goto cleanup;
250  }
251 
252  if( !load_or_generate_self_public_key__(mailbox, public_key, self_addr, NULL/*no random text data for seeding available*/) ) {
253  goto cleanup;
254  }
255 
256  success = 1;
257 
258 cleanup:
259  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
260  mrkey_unref(public_key);
261  free(self_addr);
262  return success;
263 }
264 
265 
266 /*******************************************************************************
267  * Encrypt
268  ******************************************************************************/
269 
270 
271 void mrmailbox_e2ee_encrypt(mrmailbox_t* mailbox, const clist* recipients_addr,
272  int e2ee_guaranteed, /*set if e2ee was possible on sending time; we should not degrade to transport*/
273  int encrypt_to_self, struct mailmime* in_out_message, mrmailbox_e2ee_helper_t* helper)
274 {
275  int locked = 0, col = 0, do_encrypt = 0;
276  mraheader_t* autocryptheader = mraheader_new();
277  struct mailimf_fields* imffields = NULL; /*just a pointer into mailmime structure, must not be freed*/
278  mrkeyring_t* keyring = mrkeyring_new();
279  mrkey_t* sign_key = mrkey_new();
280  MMAPString* plain = mmap_string_new("");
281  char* ctext = NULL;
282  size_t ctext_bytes = 0;
283 
284  if( helper ) { memset(helper, 0, sizeof(mrmailbox_e2ee_helper_t)); }
285 
286  if( mailbox == NULL || recipients_addr == NULL || in_out_message == NULL
287  || in_out_message->mm_parent /* libEtPan's pgp_encrypt_mime() takes the parent as the new root. We just expect the root as being given to this function. */
288  || autocryptheader == NULL || keyring==NULL || sign_key==NULL || plain == NULL || helper == NULL ) {
289  goto cleanup;
290  }
291 
292  mrsqlite3_lock(mailbox->m_sql);
293  locked = 1;
294 
295  /* load autocrypt header from db */
296  autocryptheader->m_prefer_encrypt = MRA_PE_NOPREFERENCE;
297  if( mailbox->m_e2ee_enabled ) {
298  autocryptheader->m_prefer_encrypt = MRA_PE_MUTUAL;
299  }
300 
301  autocryptheader->m_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL);
302  if( autocryptheader->m_addr == NULL ) {
303  goto cleanup;
304  }
305 
306  if( !load_or_generate_self_public_key__(mailbox, autocryptheader->m_public_key, autocryptheader->m_addr, in_out_message/*only for random-seed*/) ) {
307  goto cleanup;
308  }
309 
310  /* load peerstate information etc. */
311  if( autocryptheader->m_prefer_encrypt==MRA_PE_MUTUAL || e2ee_guaranteed )
312  {
313  do_encrypt = 1;
314  mrapeerstate_t* peerstate = mrapeerstate_new();
315  clistiter* iter1;
316  for( iter1 = clist_begin(recipients_addr); iter1!=NULL ; iter1=clist_next(iter1) ) {
317  const char* recipient_addr = clist_content(iter1);
318  if( mrapeerstate_load_from_db__(peerstate, mailbox->m_sql, recipient_addr)
319  && peerstate->m_public_key->m_binary!=NULL
320  && peerstate->m_public_key->m_bytes>0
321  && (peerstate->m_prefer_encrypt==MRA_PE_MUTUAL || e2ee_guaranteed) )
322  {
323  mrkeyring_add(keyring, peerstate->m_public_key); /* we always add all recipients (even on IMAP upload) as otherwise forwarding may fail */
324  }
325  else {
326  do_encrypt = 0;
327  break; /* if we cannot encrypt to a single recipient, we cannot encrypt the message at all */
328  }
329  }
330  mrapeerstate_unref(peerstate);
331  }
332 
333  if( do_encrypt ) {
334  mrkeyring_add(keyring, autocryptheader->m_public_key); /* we always add ourself as otherwise forwarded messages are not readable */
335  if( !mrkey_load_self_private__(sign_key, autocryptheader->m_addr, mailbox->m_sql) ) {
336  do_encrypt = 0;
337  }
338  }
339 
340  mrsqlite3_unlock(mailbox->m_sql);
341  locked = 0;
342 
343  /* encrypt message, if possible */
344  if( do_encrypt )
345  {
346  /* prepare part to encrypt */
347  mailprivacy_prepare_mime(in_out_message); /* encode quoted printable all text parts */
348 
349  struct mailmime* part_to_encrypt = in_out_message->mm_data.mm_message.mm_msg_mime;
350 
351  /* convert part to encrypt to plain text */
352  mailmime_write_mem(plain, &col, part_to_encrypt);
353  if( plain->str == NULL || plain->len<=0 ) {
354  goto cleanup;
355  }
356  //char* t1=mr_null_terminate(plain->str,plain->len);printf("PLAIN:\n%s\n",t1);free(t1); // DEBUG OUTPUT
357 
358  if( !mrpgp_pk_encrypt(mailbox, plain->str, plain->len, keyring, sign_key, 1/*use_armor*/, (void**)&ctext, &ctext_bytes) ) {
359  goto cleanup;
360  }
361  helper->m_cdata_to_free = ctext;
362  //char* t2=mr_null_terminate(ctext,ctext_bytes);printf("ENCRYPTED:\n%s\n",t2);free(t2); // DEBUG OUTPUT
363 
364  /* create MIME-structure that will contain the encrypted text */
365  struct mailmime* encrypted_part = new_data_part(NULL, 0, "multipart/encrypted", -1);
366 
367  struct mailmime_content* content = encrypted_part->mm_content_type;
368  clist_append(content->ct_parameters, mailmime_param_new_with_data("protocol", "application/pgp-encrypted"));
369 
370  static char version_content[] = "Version: 1\r\n";
371  struct mailmime* version_mime = new_data_part(version_content, strlen(version_content), "application/pgp-encrypted", MAILMIME_MECHANISM_7BIT);
372  mailmime_smart_add_part(encrypted_part, version_mime);
373 
374  struct mailmime* ctext_part = new_data_part(ctext, ctext_bytes, "application/octet-stream", MAILMIME_MECHANISM_7BIT);
375  mailmime_smart_add_part(encrypted_part, ctext_part);
376 
377  /* replace the original MIME-structure by the encrypted MIME-structure */
378  in_out_message->mm_data.mm_message.mm_msg_mime = encrypted_part;
379  encrypted_part->mm_parent = in_out_message;
380  part_to_encrypt->mm_parent = NULL;
381  mailmime_free(part_to_encrypt);
382  //MMAPString* t3=mmap_string_new("");mailmime_write_mem(t3,&col,in_out_message);char* t4=mr_null_terminate(t3->str,t3->len); printf("ENCRYPTED+MIME_ENCODED:\n%s\n",t4);free(t4);mmap_string_free(t3); // DEBUG OUTPUT
383 
384  helper->m_encryption_successfull = 1;
385  }
386 
387  /* add Autocrypt:-header to allow the recipient to send us encrypted messages back
388  (the Autocrypt:-header in the IMAP copy is needed to detect the presence of the first Autocrypt:-client if the user tries to enable multiple device
389  (we show a warning then to avoid the generation of a second keypair and to allow the user to import the first key)) */
390  if( (imffields=mr_find_mailimf_fields(in_out_message))==NULL ) {
391  goto cleanup;
392  }
393 
394  char* p = mraheader_render(autocryptheader);
395  if( p == NULL ) {
396  goto cleanup;
397  }
398  mailimf_fields_add(imffields, mailimf_field_new_custom(strdup("Autocrypt"), p/*takes ownership of pointer*/));
399 
400 cleanup:
401  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
402  mraheader_unref(autocryptheader);
403  mrkeyring_unref(keyring);
404  mrkey_unref(sign_key);
405  if( plain ) { mmap_string_free(plain); }
406 }
407 
408 
409 void mrmailbox_e2ee_thanks(mrmailbox_e2ee_helper_t* helper)
410 {
411  if( helper == NULL ) {
412  return;
413  }
414 
415  free(helper->m_cdata_to_free);
416  helper->m_cdata_to_free = NULL;
417 }
418 
419 
420 /*******************************************************************************
421  * Decrypt
422  ******************************************************************************/
423 
424 
425 static int has_decrypted_pgp_armor(const char* str__, int str_bytes)
426 {
427  const unsigned char *str_end = (const unsigned char*)str__+str_bytes, *p=(const unsigned char*)str__;
428  while( p < str_end ) {
429  if( *p > ' ' ) {
430  break;
431  }
432  p++;
433  str_bytes--;
434  }
435  if( str_bytes>27 && strncmp((const char*)p, "-----BEGIN PGP MESSAGE-----", 27)==0 ) {
436  return 1;
437  }
438  return 0;
439 }
440 
441 
442 static int decrypt_part(mrmailbox_t* mailbox,
443  struct mailmime* mime,
444  const mrkeyring_t* private_keyring,
445  const mrkey_t* public_key_for_validate, /*may be NULL*/
446  int* ret_validation_errors,
447  struct mailmime** ret_decrypted_mime)
448 {
449  struct mailmime_data* mime_data;
450  int mime_transfer_encoding = MAILMIME_MECHANISM_BINARY;
451  char* transfer_decoding_buffer = NULL; /* mmap_string_unref()'d if set */
452  const char* decoded_data = NULL; /* must not be free()'d */
453  size_t decoded_data_bytes = 0;
454  void* plain_buf = NULL;
455  size_t plain_bytes = 0;
456  int part_validation_errors = 0;
457  int sth_decrypted = 0;
458 
459  *ret_decrypted_mime = NULL;
460 
461  /* get data pointer from `mime` */
462  mime_data = mime->mm_data.mm_single;
463  if( mime_data->dt_type != MAILMIME_DATA_TEXT /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
464  || mime_data->dt_data.dt_text.dt_data == NULL
465  || mime_data->dt_data.dt_text.dt_length <= 0 ) {
466  goto cleanup;
467  }
468 
469  /* check headers in `mime` */
470  if( mime->mm_mime_fields != NULL ) {
471  clistiter* cur;
472  for( cur = clist_begin(mime->mm_mime_fields->fld_list); cur != NULL; cur = clist_next(cur) ) {
473  struct mailmime_field* field = (struct mailmime_field*)clist_content(cur);
474  if( field ) {
475  if( field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING && field->fld_data.fld_encoding ) {
476  mime_transfer_encoding = field->fld_data.fld_encoding->enc_type;
477  }
478  }
479  }
480  }
481 
482  /* regard `Content-Transfer-Encoding:` */
483  if( mime_transfer_encoding == MAILMIME_MECHANISM_7BIT
484  || mime_transfer_encoding == MAILMIME_MECHANISM_8BIT
485  || mime_transfer_encoding == MAILMIME_MECHANISM_BINARY )
486  {
487  decoded_data = mime_data->dt_data.dt_text.dt_data;
488  decoded_data_bytes = mime_data->dt_data.dt_text.dt_length;
489  if( decoded_data == NULL || decoded_data_bytes <= 0 ) {
490  goto cleanup; /* no error - but no data */
491  }
492  }
493  else
494  {
495  int r;
496  size_t current_index = 0;
497  r = mailmime_part_parse(mime_data->dt_data.dt_text.dt_data, mime_data->dt_data.dt_text.dt_length,
498  &current_index, mime_transfer_encoding,
499  &transfer_decoding_buffer, &decoded_data_bytes);
500  if( r != MAILIMF_NO_ERROR || transfer_decoding_buffer == NULL || decoded_data_bytes <= 0 ) {
501  goto cleanup;
502  }
503  decoded_data = transfer_decoding_buffer;
504  }
505 
506  /* encrypted, decoded data in decoded_data now ... */
507  if( !has_decrypted_pgp_armor(decoded_data, decoded_data_bytes) ) {
508  goto cleanup;
509  }
510 
511  if( !mrpgp_pk_decrypt(mailbox, decoded_data, decoded_data_bytes, private_keyring, public_key_for_validate, 1, &plain_buf, &plain_bytes, &part_validation_errors)
512  || plain_buf==NULL || plain_bytes<=0 ) {
513  goto cleanup;
514  }
515 
516  if( part_validation_errors ) {
517  (*ret_validation_errors) |= part_validation_errors;
518  }
519 
520  //{char* t1=mr_null_terminate(plain_buf,plain_bytes);printf("\n**********\n%s\n**********\n",t1);free(t1);}
521 
522  {
523  size_t index = 0;
524  struct mailmime* decrypted_mime = NULL;
525  if( mailmime_parse(plain_buf, plain_bytes, &index, &decrypted_mime)!=MAIL_NO_ERROR
526  || decrypted_mime == NULL ) {
527  if(decrypted_mime) {mailmime_free(decrypted_mime);}
528  goto cleanup;
529  }
530 
531  //mr_print_mime(new_mime);
532 
533  *ret_decrypted_mime = decrypted_mime;
534  sth_decrypted = 1;
535  }
536 
537  //mailmime_substitute(mime, new_mime);
538  //s. mailprivacy_gnupg.c::pgp_decrypt()
539 
540 cleanup:
541  if( transfer_decoding_buffer ) {
542  mmap_string_unref(transfer_decoding_buffer);
543  }
544  return sth_decrypted;
545 }
546 
547 
548 static int decrypt_recursive(mrmailbox_t* mailbox,
549  struct mailmime* mime,
550  const mrkeyring_t* private_keyring,
551  const mrkey_t* public_key_for_validate,
552  int* ret_validation_errors)
553 {
554  struct mailmime_content* ct;
555  clistiter* cur;
556 
557  if( mailbox == NULL || mime == NULL ) {
558  return 0;
559  }
560 
561  if( mime->mm_type == MAILMIME_MULTIPLE )
562  {
563  ct = mime->mm_content_type;
564  if( ct && ct->ct_subtype && strcmp(ct->ct_subtype, "encrypted")==0 ) {
565  /* decrypt "multipart/encrypted" -- child parts are eg. "application/pgp-encrypted" (uninteresting, version only),
566  "application/octet-stream" (the interesting data part) and optional, unencrypted help files */
567  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
568  struct mailmime* decrypted_mime = NULL;
569  if( decrypt_part(mailbox, (struct mailmime*)clist_content(cur), private_keyring, public_key_for_validate, ret_validation_errors, &decrypted_mime) )
570  {
571  mailmime_substitute(mime, decrypted_mime);
572  mailmime_free(mime);
573  return 1; /* sth. decrypted, start over from root searching for encrypted parts */
574  }
575  }
576  }
577  else {
578  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
579  if( decrypt_recursive(mailbox, (struct mailmime*)clist_content(cur), private_keyring, public_key_for_validate, ret_validation_errors) ) {
580  return 1; /* sth. decrypted, start over from root searching for encrypted parts */
581  }
582  }
583  }
584  }
585  else if( mime->mm_type == MAILMIME_MESSAGE )
586  {
587  if( decrypt_recursive(mailbox, mime->mm_data.mm_message.mm_msg_mime, private_keyring, public_key_for_validate, ret_validation_errors) ) {
588  return 1; /* sth. decrypted, start over from root searching for encrypted parts */
589  }
590  }
591 
592  return 0;
593 }
594 
595 
596 static int contains_report(struct mailmime* mime)
597 {
598  /* returns true if the mime structure contains a multipart/report
599  (as reports are often unencrypted, we do not reset the Autocrypt header in this case)
600  (however, MUA should be encouraged to encrpyt multipart/reports as well so that we can use the normal Autocrypt processing) */
601  if( mime->mm_type == MAILMIME_MULTIPLE )
602  {
603  if( mime->mm_content_type->ct_type->tp_type==MAILMIME_TYPE_COMPOSITE_TYPE
604  && mime->mm_content_type->ct_type->tp_data.tp_composite_type->ct_type == MAILMIME_COMPOSITE_TYPE_MULTIPART
605  && strcmp(mime->mm_content_type->ct_subtype, "report")==0 ) {
606  return 1;
607  }
608 
609  clistiter* cur;
610  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
611  if( contains_report((struct mailmime*)clist_content(cur)) ) {
612  return 1;
613  }
614  }
615  }
616  else if( mime->mm_type == MAILMIME_MESSAGE )
617  {
618  if( contains_report(mime->mm_data.mm_message.mm_msg_mime) ) {
619  return 1;
620  }
621  }
622 
623  return 0;
624 }
625 
626 
627 int mrmailbox_e2ee_decrypt(mrmailbox_t* mailbox, struct mailmime* in_out_message, int* ret_validation_errors)
628 {
629  /* return values: 0=nothing to decrypt/cannot decrypt, 1=sth. decrypted
630  (to detect parts that could not be decrypted, simply look for left "multipart/encrypted" MIME types */
631  struct mailimf_fields* imffields = mr_find_mailimf_fields(in_out_message); /*just a pointer into mailmime structure, must not be freed*/
632  mraheader_t* autocryptheader = NULL;
633  time_t message_time = 0;
634  mrapeerstate_t* peerstate = mrapeerstate_new();
635  int locked = 0;
636  char* from = NULL, *self_addr = NULL;
637  mrkeyring_t* private_keyring = mrkeyring_new();
638  int sth_decrypted = 0;
639 
640  if( mailbox==NULL || in_out_message==NULL || ret_validation_errors==NULL
641  || imffields==NULL || peerstate==NULL || private_keyring==NULL ) {
642  goto cleanup;
643  }
644 
645  /* Autocrypt preparations:
646  - Set message_time and from (both may be unset)
647  - Get the autocrypt header, if any.
648  - Do not abort on errors - we should try at last the decyption below */
649  if( imffields )
650  {
651  struct mailimf_field* field = mr_find_mailimf_field(imffields, MAILIMF_FIELD_FROM);
652  if( field && field->fld_data.fld_from ) {
653  from = mr_find_first_addr(field->fld_data.fld_from->frm_mb_list);
654  }
655 
656  field = mr_find_mailimf_field(imffields, MAILIMF_FIELD_ORIG_DATE);
657  if( field && field->fld_data.fld_orig_date ) {
658  struct mailimf_orig_date* orig_date = field->fld_data.fld_orig_date;
659  if( orig_date ) {
660  message_time = mr_timestamp_from_date(orig_date->dt_date_time); /* is not yet checked against bad times! */
661  if( message_time != MR_INVALID_TIMESTAMP && message_time > time(NULL) ) {
662  message_time = time(NULL);
663  }
664  }
665  }
666  }
667 
668  autocryptheader = mraheader_new_from_imffields(from, imffields);
669  if( autocryptheader ) {
670  if( !mrpgp_is_valid_key(mailbox, autocryptheader->m_public_key) ) {
671  mraheader_unref(autocryptheader);
672  autocryptheader = NULL;
673  }
674  }
675 
676  /* modify the peerstate (eg. if there is a peer but not autocrypt header, stop encryption) */
677  mrsqlite3_lock(mailbox->m_sql);
678  locked = 1;
679 
680  /* apply Autocrypt:-header */
681  if( message_time > 0
682  && from )
683  {
684  if( mrapeerstate_load_from_db__(peerstate, mailbox->m_sql, from) ) {
685  if( autocryptheader ) {
686  mrapeerstate_apply_header(peerstate, autocryptheader, message_time);
687  mrapeerstate_save_to_db__(peerstate, mailbox->m_sql, 0/*no not create*/);
688  }
689  else {
690  if( message_time > peerstate->m_last_seen_autocrypt
691  && !contains_report(in_out_message) /*reports are ususally not encrpyted; do not degrade decryption then*/ ){
692  mrapeerstate_degrade_encryption(peerstate, message_time);
693  mrapeerstate_save_to_db__(peerstate, mailbox->m_sql, 0/*no not create*/);
694  }
695  }
696  }
697  else if( autocryptheader ) {
698  mrapeerstate_init_from_header(peerstate, autocryptheader, message_time);
699  mrapeerstate_save_to_db__(peerstate, mailbox->m_sql, 1/*create*/);
700  }
701  }
702 
703  /* load private key for decryption */
704  if( (self_addr=mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL))==NULL ) {
705  goto cleanup;
706  }
707 
708  if( !mrkeyring_load_self_private_for_decrypting__(private_keyring, self_addr, mailbox->m_sql) ) {
709  goto cleanup;
710  }
711 
712  /* if not yet done, load peer with public key for verification (should be last as the peer may be modified above) */
713  if( peerstate->m_public_key->m_bytes <= 0 ) {
714  mrapeerstate_load_from_db__(peerstate, mailbox->m_sql, from);
715  }
716 
717  mrsqlite3_unlock(mailbox->m_sql);
718  locked = 0;
719 
720  /* finally, decrypt. If sth. was decrypted, decrypt_recursive() returns "true" and we start over to decrypt maybe just added parts. */
721  *ret_validation_errors = 0;
722  int avoid_deadlock = 10;
723  while( avoid_deadlock > 0 ) {
724  if( !decrypt_recursive(mailbox, in_out_message, private_keyring, peerstate->m_public_key->m_bytes>0? peerstate->m_public_key : NULL, ret_validation_errors) ) {
725  break;
726  }
727  sth_decrypted = 1;
728  avoid_deadlock--;
729  }
730 
731  if( *ret_validation_errors == 0 ) {
732  if( peerstate->m_prefer_encrypt != MRA_PE_MUTUAL ) {
733  *ret_validation_errors = MR_VALIDATE_NOT_MUTUAL; /* this results in MRP_ERRONEOUS_E2EE instead of MRP_GUARANTEE_E2EE and finally in mrmsg_show_padlock() returning `false` */
734  }
735  }
736 
737  //mr_print_mime(in_out_message);
738 
739 cleanup:
740  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
741  mraheader_unref(autocryptheader);
742  mrapeerstate_unref(peerstate);
743  mrkeyring_unref(private_keyring);
744  free(from);
745  free(self_addr);
746  return sth_decrypted;
747 }
748 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrmailbox__imex_8c_source.html b/docs/user/html/mrmailbox__imex_8c_source.html deleted file mode 100644 index 2ddf3383..00000000 --- a/docs/user/html/mrmailbox__imex_8c_source.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmailbox_imex.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmailbox_imex.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <dirent.h>
24 #include <openssl/rand.h>
25 #include <libetpan/mmapstring.h>
26 #include <netpgp-extra.h>
27 #include "mrmailbox_internal.h"
28 #include "mrmimeparser.h"
29 #include "mrosnative.h"
30 #include "mrloginparam.h"
31 #include "mraheader.h"
32 #include "mrapeerstate.h"
33 #include "mrpgp.h"
34 
35 static int s_imex_do_exit = 1; /* the value 1 avoids MR_IMEX_CANCEL from stopping already stopped threads */
36 
37 
38 /*******************************************************************************
39  * Import
40  ******************************************************************************/
41 
42 
43 static int poke_public_key(mrmailbox_t* mailbox, const char* addr, const char* public_key_file)
44 {
45  /* mainly for testing: if the partner does not support Autocrypt,
46  encryption is disabled as soon as the first messages comes from the partner */
47  mraheader_t* header = mraheader_new();
48  mrapeerstate_t* peerstate = mrapeerstate_new();
49  int locked = 0, success = 0;
50 
51  if( addr==NULL || public_key_file==NULL || peerstate==NULL || header==NULL ) {
52  goto cleanup;
53  }
54 
55  /* create a fake autocrypt header */
56  header->m_addr = safe_strdup(addr);
57  header->m_prefer_encrypt = MRA_PE_MUTUAL;
58  if( !mrkey_set_from_file(header->m_public_key, public_key_file, mailbox)
59  || !mrpgp_is_valid_key(mailbox, header->m_public_key) ) {
60  mrmailbox_log_warning(mailbox, 0, "No valid key found in \"%s\".", public_key_file);
61  goto cleanup;
62  }
63 
64  /* update/create peerstate */
65  mrsqlite3_lock(mailbox->m_sql);
66  locked = 1;
67 
68  if( mrapeerstate_load_from_db__(peerstate, mailbox->m_sql, addr) ) {
69  mrapeerstate_apply_header(peerstate, header, time(NULL));
70  mrapeerstate_save_to_db__(peerstate, mailbox->m_sql, 0);
71  }
72  else {
73  mrapeerstate_init_from_header(peerstate, header, time(NULL));
74  mrapeerstate_save_to_db__(peerstate, mailbox->m_sql, 1);
75  }
76 
77  success = 1;
78 
79 cleanup:
80  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
81  mrapeerstate_unref(peerstate);
82  mraheader_unref(header);
83  return success;
84 }
85 
86 
100 int mrmailbox_poke_spec(mrmailbox_t* mailbox, const char* spec)
101 {
102  int success = 0;
103  char* real_spec = NULL;
104  char* suffix = NULL;
105  DIR* dir = NULL;
106  struct dirent* dir_entry;
107  int read_cnt = 0;
108  char* name;
109 
110  if( mailbox == NULL ) {
111  return 0;
112  }
113 
114  if( !mrsqlite3_is_open(mailbox->m_sql) ) {
115  mrmailbox_log_error(mailbox, 0, "Import: Database not opened.");
116  goto cleanup;
117  }
118 
119  /* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
120  if( spec )
121  {
122  real_spec = safe_strdup(spec);
123  mrsqlite3_lock(mailbox->m_sql);
124  mrsqlite3_set_config__(mailbox->m_sql, "import_spec", real_spec);
125  mrsqlite3_unlock(mailbox->m_sql);
126  }
127  else {
128  mrsqlite3_lock(mailbox->m_sql);
129  real_spec = mrsqlite3_get_config__(mailbox->m_sql, "import_spec", NULL); /* may still NULL */
130  mrsqlite3_unlock(mailbox->m_sql);
131  if( real_spec == NULL ) {
132  mrmailbox_log_error(mailbox, 0, "Import: No file or folder given.");
133  goto cleanup;
134  }
135  }
136 
137  suffix = mr_get_filesuffix_lc(real_spec);
138  if( suffix && strcmp(suffix, "eml")==0 ) {
139  /* import a single file */
140  if( mrmailbox_poke_eml_file(mailbox, real_spec) ) { /* errors are logged in any case */
141  read_cnt++;
142  }
143  }
144  else if( suffix && (strcmp(suffix, "pem")==0||strcmp(suffix, "asc")==0) ) {
145  /* import a publix key */
146  char* separator = strchr(real_spec, ' ');
147  if( separator==NULL ) {
148  mrmailbox_log_error(mailbox, 0, "Import: Key files must be specified as \"<addr> <key-file>\".");
149  goto cleanup;
150  }
151  *separator = 0;
152  if( poke_public_key(mailbox, real_spec, separator+1) ) {
153  read_cnt++;
154  }
155  *separator = ' ';
156  }
157  else {
158  /* import a directory */
159  if( (dir=opendir(real_spec))==NULL ) {
160  mrmailbox_log_error(mailbox, 0, "Import: Cannot open directory \"%s\".", real_spec);
161  goto cleanup;
162  }
163 
164  while( (dir_entry=readdir(dir))!=NULL ) {
165  name = dir_entry->d_name; /* name without path; may also be `.` or `..` */
166  if( strlen(name)>=4 && strcmp(&name[strlen(name)-4], ".eml")==0 ) {
167  char* path_plus_name = mr_mprintf("%s/%s", real_spec, name);
168  mrmailbox_log_info(mailbox, 0, "Import: %s", path_plus_name);
169  if( mrmailbox_poke_eml_file(mailbox, path_plus_name) ) { /* no abort on single errors errors are logged in any case */
170  read_cnt++;
171  }
172  free(path_plus_name);
173  }
174  }
175  }
176 
177  mrmailbox_log_info(mailbox, 0, "Import: %i items read from \"%s\".", read_cnt, real_spec);
178  if( read_cnt > 0 ) {
179  mailbox->m_cb(mailbox, MR_EVENT_MSGS_CHANGED, 0, 0); /* even if read_cnt>0, the number of messages added to the database may be 0. While we regard this issue using IMAP, we ignore it here. */
180  }
181 
182  /* success */
183  success = 1;
184 
185  /* cleanup */
186 cleanup:
187  if( dir ) {
188  closedir(dir);
189  }
190  free(real_spec);
191  free(suffix);
192  return success;
193 }
194 
195 
196 static int import_self_keys(mrmailbox_t* mailbox, const char* dir_name)
197 {
198  /* hint: even if we switch to import Autocrypt Setup files, we should leave the possibility to import
199  plain ASC keys, at least keys without a password, if we do not want to implement a password entry function.
200  Importing ASC keys is useful to use keys in Delta Chat used by any other non-Autocrypt-PGP implementation.
201 
202  Maybe we should make the "default" key handlong also a little bit smarter
203  (currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
204 
205  int imported_count = 0, locked = 0;
206  DIR* dir_handle = NULL;
207  struct dirent* dir_entry = NULL;
208  char* suffix = NULL;
209  char* path_plus_name = NULL;
210  mrkey_t* private_key = mrkey_new();
211  mrkey_t* public_key = mrkey_new();
212  sqlite3_stmt* stmt = NULL;
213  char* self_addr = NULL;
214  int set_default = 0;
215 
216  if( mailbox==NULL || dir_name==NULL ) {
217  goto cleanup;
218  }
219 
220  if( (dir_handle=opendir(dir_name))==NULL ) {
221  mrmailbox_log_error(mailbox, 0, "Import: Cannot open directory \"%s\".", dir_name);
222  goto cleanup;
223  }
224 
225  while( (dir_entry=readdir(dir_handle))!=NULL )
226  {
227  free(suffix);
228  suffix = mr_get_filesuffix_lc(dir_entry->d_name);
229  if( suffix==NULL || strcmp(suffix, "asc")!=0 ) {
230  continue;
231  }
232 
233  free(path_plus_name);
234  path_plus_name = mr_mprintf("%s/%s", dir_name, dir_entry->d_name/* name without path; may also be `.` or `..` */);
235  mrmailbox_log_info(mailbox, 0, "Checking: %s", path_plus_name);
236  if( !mrkey_set_from_file(private_key, path_plus_name, mailbox) ) {
237  mrmailbox_log_error(mailbox, 0, "Cannot read key from \"%s\".", path_plus_name);
238  continue;
239  }
240 
241  if( private_key->m_type!=MR_PRIVATE ) {
242  continue; /* this is no error but quite normal as we always export the public keys together with the private ones */
243  }
244 
245  if( !mrpgp_is_valid_key(mailbox, private_key) ) {
246  mrmailbox_log_error(mailbox, 0, "\"%s\" is no valid key.", path_plus_name);
247  continue;
248  }
249 
250  if( !mrpgp_split_key(mailbox, private_key, public_key) ) {
251  mrmailbox_log_error(mailbox, 0, "\"%s\" seems not to contain a private key.", path_plus_name);
252  continue;
253  }
254 
255  set_default = 1;
256  if( strstr(dir_entry->d_name, "legacy")!=NULL ) {
257  set_default = 0; /* a key with "legacy" in its name is not made default; this may result in a keychain with _no_ default, however, this is no problem, as this will create a default key later */
258  }
259 
260  /* add keypair as default; before this, delete other keypairs with the same binary key and reset defaults */
261  mrsqlite3_lock(mailbox->m_sql);
262  locked = 1;
263 
264  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "DELETE FROM keypairs WHERE public_key=? OR private_key=?;");
265  sqlite3_bind_blob (stmt, 1, public_key->m_binary, public_key->m_bytes, SQLITE_STATIC);
266  sqlite3_bind_blob (stmt, 2, private_key->m_binary, private_key->m_bytes, SQLITE_STATIC);
267  sqlite3_step(stmt);
268  sqlite3_finalize(stmt);
269  stmt = NULL;
270 
271  if( set_default ) {
272  mrsqlite3_execute__(mailbox->m_sql, "UPDATE keypairs SET is_default=0;"); /* if the new key should be the default key, all other should not */
273  }
274 
275  free(self_addr);
276  self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL);
277  if( !mrkey_save_self_keypair__(public_key, private_key, self_addr, set_default, mailbox->m_sql) ) {
278  mrmailbox_log_error(mailbox, 0, "Cannot save keypair.");
279  goto cleanup;
280  }
281 
282  imported_count++;
283 
284  mrsqlite3_unlock(mailbox->m_sql);
285  locked = 0;
286  }
287 
288  if( imported_count == 0 ) {
289  mrmailbox_log_error(mailbox, 0, "No private keys found in \"%s\".", dir_name);
290  goto cleanup;
291  }
292 
293 cleanup:
294  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
295  if( dir_handle ) { closedir(dir_handle); }
296  free(suffix);
297  free(path_plus_name);
298  mrkey_unref(private_key);
299  mrkey_unref(public_key);
300  if( stmt ) { sqlite3_finalize(stmt); }
301  free(self_addr);
302  return imported_count;
303 }
304 
305 
306 /*******************************************************************************
307  * Export keys
308  ******************************************************************************/
309 
310 
311 static void export_key_to_asc_file(mrmailbox_t* mailbox, const char* dir, int id, const mrkey_t* key, int is_default)
312 {
313  char* file_content = mrkey_render_asc(key, NULL);
314  char* file_name;
315  if( is_default ) {
316  file_name = mr_mprintf("%s/%s-key-default.asc", dir, key->m_type==MR_PUBLIC? "public" : "private");
317  }
318  else {
319  file_name = mr_mprintf("%s/%s-key-%i.asc", dir, key->m_type==MR_PUBLIC? "public" : "private", id);
320  }
321  mrmailbox_log_info(mailbox, 0, "Exporting key %s", file_name);
322  mr_delete_file(file_name, mailbox);
323  if( !mr_write_file(file_name, file_content, strlen(file_content), mailbox) ) {
324  mrmailbox_log_error(mailbox, 0, "Cannot write key to %s", file_name);
325  }
326  else {
327  mailbox->m_cb(mailbox, MR_EVENT_IMEX_FILE_WRITTEN, (uintptr_t)file_name, (uintptr_t)"application/pgp-keys");
328  }
329  free(file_content);
330  free(file_name);
331 }
332 
333 
334 static int export_self_keys(mrmailbox_t* mailbox, const char* dir)
335 {
336  int success = 0;
337  sqlite3_stmt* stmt = NULL;
338  int id = 0, is_default = 0;
339  mrkey_t* public_key = mrkey_new();
340  mrkey_t* private_key = mrkey_new();
341  int locked = 0;
342 
343  mrsqlite3_lock(mailbox->m_sql);
344  locked = 1;
345 
346  if( (stmt=mrsqlite3_prepare_v2_(mailbox->m_sql, "SELECT id, public_key, private_key, is_default FROM keypairs;"))==NULL ) {
347  goto cleanup;
348  }
349 
350  while( sqlite3_step(stmt)==SQLITE_ROW ) {
351  id = sqlite3_column_int( stmt, 0 );
352  mrkey_set_from_stmt(public_key, stmt, 1, MR_PUBLIC);
353  mrkey_set_from_stmt(private_key, stmt, 2, MR_PRIVATE);
354  is_default = sqlite3_column_int( stmt, 3 );
355  export_key_to_asc_file(mailbox, dir, id, public_key, is_default);
356  export_key_to_asc_file(mailbox, dir, id, private_key, is_default);
357  }
358 
359  success = 1;
360 
361 cleanup:
362  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
363  if( stmt ) { sqlite3_finalize(stmt); }
364  mrkey_unref(public_key);
365  mrkey_unref(private_key);
366  return success;
367 }
368 
369 
370 
371 /*******************************************************************************
372  * Export setup file
373  ******************************************************************************/
374 
375 
376 /* a complete Autocrypt Setup Message looks like the following
377 
378 To: me@mydomain.com
379 From: me@mydomain.com
380 Autocrypt-Setup-Message: v1
381 Content-type: multipart/mixed; boundary="==break1=="
382 
383  --==break1==
384  Content-Type: text/plain
385 
386  This is the Autocrypt setup message.
387 
388  --==break1==
389  Content-Type: application/autocrypt-key-backup
390  Content-Disposition: attachment; filename="autocrypt-key-backup.html"
391 
392  <html>
393  <body>
394  <p>
395  This is the Autocrypt setup file used to transfer keys between clients.
396  </p>
397  <pre>
398  -----BEGIN PGP MESSAGE-----
399  Version: BCPG v1.53
400  Passphrase-Format: numeric9x4
401  Passphrase-Begin: 12
402 
403  hQIMAxC7JraDy7DVAQ//SK1NltM+r6uRf2BJEg+rnpmiwfAEIiopU0LeOQ6ysmZ0
404  CLlfUKAcryaxndj4sBsxLllXWzlNiFDHWw4OOUEZAZd8YRbOPfVq2I8+W4jO3Moe
405  -----END PGP MESSAGE-----
406  </pre>
407  </body>
408  </html>
409  --==break1==--
410 
411 The encrypted message part contains:
412 
413  -----BEGIN PGP PRIVATE KEY BLOCK-----
414  Autocrypt-Prefer-Encrypt: mutual
415 
416  xcLYBFke7/8BCAD0TTmX9WJm9elc7/xrT4/lyzUDMLbuAuUqRINtCoUQPT2P3Snfx/jou1YcmjDgwT
417  Ny9ddjyLcdSKL/aR6qQ1UBvlC5xtriU/7hZV6OZEmW2ckF7UgGd6ajE+UEjUwJg2+eKxGWFGuZ1P7a
418  4Av1NXLayZDsYa91RC5hCsj+umLN2s+68ps5pzLP3NoK2zIFGoCRncgGI/pTAVmYDirhVoKh14hCh5
419  .....
420  -----END PGP PRIVATE KEY BLOCK-----
421 
422 mrmailbox_render_keys_to_html() renders the part after the second `-==break1==` part in this example. */
423 int mrmailbox_render_keys_to_html(mrmailbox_t* mailbox, const char* passphrase, char** ret_msg)
424 {
425  int success = 0, locked = 0;
426  sqlite3_stmt* stmt = NULL;
427  char* self_addr = NULL;
428  mrkey_t* curr_private_key = mrkey_new();
429 
430  char passphrase_begin[8];
431  uint8_t salt[PGP_SALT_SIZE];
432  #define AES_KEY_LENGTH 16
433  uint8_t key[AES_KEY_LENGTH];
434 
435  pgp_output_t* payload_output = NULL;
436  pgp_memory_t* payload_mem = NULL;
437 
438  pgp_output_t* encr_output = NULL;
439  pgp_memory_t* encr_mem = NULL;
440  char* encr_string = NULL;
441 
442 
443  if( mailbox==NULL || passphrase==NULL || ret_msg==NULL
444  || strlen(passphrase)<2 || *ret_msg!=NULL || curr_private_key==NULL ) {
445  goto cleanup;
446  }
447 
448  strncpy(passphrase_begin, passphrase, 2);
449  passphrase_begin[2] = 0;
450 
451  /* create the payload */
452 
453  {
454  mrsqlite3_lock(mailbox->m_sql);
455  locked = 1;
456 
457  self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", NULL);
458  mrkey_load_self_private__(curr_private_key, self_addr, mailbox->m_sql);
459 
460  char* payload_key_asc = mrkey_render_asc(curr_private_key, mailbox->m_e2ee_enabled? "Autocrypt-Prefer-Encrypt: mutual\r\n" : NULL);
461  if( payload_key_asc == NULL ) {
462  goto cleanup;
463  }
464 
465  mrsqlite3_unlock(mailbox->m_sql);
466  locked = 0;
467 
468  //printf("\n~~~~~~~~~~~~~~~~~~~~SETUP-PAYLOAD~~~~~~~~~~~~~~~~~~~~\n%s~~~~~~~~~~~~~~~~~~~~/SETUP-PAYLOAD~~~~~~~~~~~~~~~~~~~~\n",key_asc); // DEBUG OUTPUT
469 
470 
471  /* put the payload into a literal data packet which will be encrypted then, see RFC 4880, 5.7 :
472  "When it has been decrypted, it contains other packets (usually a literal data packet or compressed data
473  packet, but in theory other Symmetrically Encrypted Data packets or sequences of packets that form whole OpenPGP messages)" */
474 
475  pgp_setup_memory_write(&payload_output, &payload_mem, 128);
476  pgp_write_litdata(payload_output, (const uint8_t*)payload_key_asc, strlen(payload_key_asc), PGP_LDT_BINARY);
477 
478  free(payload_key_asc);
479  }
480 
481 
482  /* create salt for the key */
483  pgp_random(salt, PGP_SALT_SIZE);
484 
485  /* S2K */
486 
487  int s2k_spec = PGP_S2KS_SIMPLE; // 0=simple, 1=salted, 3=salted+iterated
488  int s2k_iter_id = 0; // ~1000 iterations
489 
490  /* create key from setup-code using OpenPGP's salted+iterated S2K (String-to-key)
491  (from netpgp/create.c) */
492 
493  {
494  unsigned done = 0;
495  unsigned i = 0;
496  int passphrase_len = strlen(passphrase);
497  pgp_hash_t hash;
498  for (done = 0, i = 0; done < AES_KEY_LENGTH; i++) {
499  unsigned hashsize;
500  unsigned j;
501  unsigned needed;
502  unsigned size;
503  uint8_t zero = 0;
504  uint8_t *hashed;
505 
506  /* Hard-coded SHA1 for session key */
507  pgp_hash_any(&hash, PGP_HASH_SHA1);
508  hashsize = pgp_hash_size(PGP_HASH_SHA1);
509  needed = AES_KEY_LENGTH - done;
510  size = MR_MIN(needed, hashsize);
511  if ((hashed = calloc(1, hashsize)) == NULL) {
512  (void) fprintf(stderr, "write_seckey_body: bad alloc\n");
513  return 0;
514  }
515  if (!hash.init(&hash)) {
516  (void) fprintf(stderr, "write_seckey_body: bad alloc\n");
517  free(hashed);
518  return 0;
519  }
520 
521  /* preload if iterating */
522  for (j = 0; j < i; j++) {
523  /*
524  * Coverity shows a DEADCODE error on this
525  * line. This is expected since the hardcoded
526  * use of SHA1 and CAST5 means that it will
527  * not used. This will change however when
528  * other algorithms are supported.
529  */
530  hash.add(&hash, &zero, 1);
531  }
532 
533  if (s2k_spec & PGP_S2KS_SALTED) {
534  hash.add(&hash, salt, PGP_SALT_SIZE);
535  }
536 
537  hash.add(&hash, (uint8_t*)passphrase, (unsigned)passphrase_len);
538  hash.finish(&hash, hashed);
539 
540  /*
541  * if more in hash than is needed by session key, use
542  * the leftmost octets
543  */
544  (void) memcpy(&key[i * hashsize], hashed, (unsigned)size);
545  done += (unsigned)size;
546  free(hashed);
547  if (done > AES_KEY_LENGTH) {
548  (void) fprintf(stderr,
549  "write_seckey_body: short add\n");
550  return 0;
551  }
552  }
553  }
554 
555  /* encrypt the payload using the key using AES-128 and put it into
556  OpenPGP's "Symmetric-Key Encrypted Session Key" (Tag 3, https://tools.ietf.org/html/rfc4880#section-5.3 ) followed by
557  OpenPGP's "Symmetrically Encrypted Data Packet" (Tag 9, https://tools.ietf.org/html/rfc4880#section-5.7 ) */
558 
559  pgp_setup_memory_write(&encr_output, &encr_mem, 128);
560  pgp_writer_push_armor_msg(encr_output);
561 
562  /* Tag 3 */
563  pgp_write_ptag (encr_output, PGP_PTAG_CT_SK_SESSION_KEY);
564  pgp_write_length (encr_output, 1/*version*/ + 1/*algo*/ + /*S2K*/1+1+((s2k_spec&PGP_S2KS_SALTED)?PGP_SALT_SIZE:0)+((s2k_spec==PGP_S2KS_ITERATED_AND_SALTED)?1:0) );
565 
566  pgp_write_scalar (encr_output, 4, 1); // 1 octet: version
567  pgp_write_scalar (encr_output, PGP_SA_AES_128, 1); // 1 octet: symm. algo
568 
569  pgp_write_scalar (encr_output, s2k_spec, 1); // 1 octet
570  pgp_write_scalar (encr_output, PGP_HASH_SHA1, 1); // 1 octet: S2 hash algo
571  if( s2k_spec&PGP_S2KS_SALTED ) {
572  pgp_write (encr_output, salt, PGP_SALT_SIZE); // 8 octets: salt
573  }
574  if( s2k_spec==PGP_S2KS_ITERATED_AND_SALTED ) {
575  pgp_write_scalar (encr_output, s2k_iter_id, 1); // 1 octets
576  }
577 
578  for(int j=0; j<AES_KEY_LENGTH; j++) {
579  printf("%02x", key[j]);
580  }
581  printf("\n----------------\n");
582 
583  /* Tag 18 */
584  pgp_write_symm_enc_data((const uint8_t*)payload_mem->buf, payload_mem->length, PGP_SA_AES_128, key, encr_output);
585 
586  /* done with symmetric key block */
587  pgp_writer_close(encr_output);
588  encr_string = mr_null_terminate((const char*)encr_mem->buf, encr_mem->length);
589 
590  //printf("\n~~~~~~~~~~~~~~~~~~~~SYMMETRICALLY ENCRYPTED~~~~~~~~~~~~~~~~~~~~\n%s~~~~~~~~~~~~~~~~~~~~/SYMMETRICALLY ENCRYPTED~~~~~~~~~~~~~~~~~~~~\n",encr_string); // DEBUG OUTPUT
591 
592 
593  /* add additional header to armored block */
594 
595  #define LINEEND "\r\n" /* use the same lineends as the PGP armored data */
596  {
597  char* replacement = mr_mprintf("-----BEGIN PGP MESSAGE-----" LINEEND
598  "Passphrase-Format: numeric9x4" LINEEND
599  "Passphrase-Begin: %s", passphrase_begin);
600  mr_str_replace(&encr_string, "-----BEGIN PGP MESSAGE-----", replacement);
601  free(replacement);
602  }
603 
604  /* wrap HTML-commands with instructions around the encrypted payload */
605 
606  *ret_msg = mr_mprintf(
607  "<!DOCTYPE html>" LINEEND
608  "<html>" LINEEND
609  "<head>" LINEEND
610  "<title>Autocrypt setup file</title>" LINEEND
611  "</head>" LINEEND
612  "<body>" LINEEND
613  "<h1>Autocrypt setup file</h1>" LINEEND
614  "<p>This is the <a href=\"https://autocrypt.org\">Autocrypt</a> setup file used to transfer your secret key between clients.</p>" LINEEND
615  "<p>To decrypt the key, you need the setup code that was shown to you when this file was created. Hint: The setup code starts with: <em>%s</em></p>" LINEEND
616  "<h2>Encrypted key</h2>" LINEEND
617  "<pre>" LINEEND
618  "%s" LINEEND
619  "</pre>" LINEEND
620  "</body>" LINEEND
621  "</html>" LINEEND,
622  passphrase_begin,
623  encr_string);
624 
625  success = 1;
626 
627 cleanup:
628  if( stmt ) { sqlite3_finalize(stmt); }
629  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
630 
631  if( payload_output ) { pgp_output_delete(payload_output); }
632  if( payload_mem ) { pgp_memory_free(payload_mem); }
633 
634  if( encr_output ) { pgp_output_delete(encr_output); }
635  if( encr_mem ) { pgp_memory_free(encr_mem); }
636  free(encr_string);
637  free(self_addr);
638  return success;
639 }
640 
641 
642 static int export_setup_file(mrmailbox_t* mailbox, const char* dir, const char* setup_code)
643 {
644  int success = 0;
645  char* file_content = NULL;
646  char* file_name = mr_mprintf("%s/autocrypt-key-backup.html", dir);
647 
648  if( !mrmailbox_render_keys_to_html(mailbox, setup_code, &file_content) || file_content==NULL ) {
649  mrmailbox_log_error(mailbox, 0, "Cannot generate Autocrypt setup file in %s", file_name);
650  goto cleanup;
651  }
652 
653  if( !mr_write_file(file_name, file_content, strlen(file_content), mailbox) ) {
654  mrmailbox_log_error(mailbox, 0, "Cannot write keys to %s", file_name);
655  }
656  else {
657  mailbox->m_cb(mailbox, MR_EVENT_IMEX_FILE_WRITTEN, (uintptr_t)file_name, (uintptr_t)"application/autocrypt-key-backup");
658  }
659 
660  success = 1;
661 
662 cleanup:
663  free(file_content);
664  free(file_name);
665  return success;
666 }
667 
668 
669 
670 /*******************************************************************************
671  * Export backup
672  ******************************************************************************/
673 
674 
675 /* the FILE_PROGRESS macro calls the callback with the permille of files processed.
676 The macro avoids weird values of 0% or 100% while still working. */
677 #define FILE_PROGRESS \
678  processed_files_count++; \
679  int permille = (processed_files_count*1000)/total_files_count; \
680  if( permille < 10 ) { permille = 10; } \
681  if( permille > 990 ) { permille = 990; } \
682  mailbox->m_cb(mailbox, MR_EVENT_IMEX_PROGRESS, permille, 0);
683 
684 
685 static int export_backup(mrmailbox_t* mailbox, const char* dir)
686 {
687  int success = 0, locked = 0, closed = 0;
688  char* dest_pathNfilename = NULL;
689  mrsqlite3_t* dest_sql = NULL;
690  time_t now = time(NULL);
691  DIR* dir_handle = NULL;
692  struct dirent* dir_entry;
693  int prefix_len = strlen(MR_BAK_PREFIX);
694  int suffix_len = strlen(MR_BAK_SUFFIX);
695  char* curr_pathNfilename = NULL;
696  void* buf = NULL;
697  size_t buf_bytes = 0;
698  sqlite3_stmt* stmt = NULL;
699  int total_files_count = 0, processed_files_count = 0;
700  int delete_dest_file = 0;
701 
702  /* get a fine backup file name (the name includes the date so that multiple backup instances are possible) */
703  {
704  struct tm* timeinfo;
705  char buffer[256];
706  timeinfo = localtime(&now);
707  strftime(buffer, 256, MR_BAK_PREFIX "-%Y-%m-%d." MR_BAK_SUFFIX, timeinfo);
708  if( (dest_pathNfilename=mr_get_fine_pathNfilename(dir, buffer))==NULL ) {
709  mrmailbox_log_error(mailbox, 0, "Cannot get backup file name.");
710  goto cleanup;
711  }
712  }
713 
714  /* temporary lock and close the source (we just make a copy of the whole file, this is the fastest and easiest approach) */
715  mrsqlite3_lock(mailbox->m_sql);
716  locked = 1;
717  mrsqlite3_close__(mailbox->m_sql);
718  closed = 1;
719 
720  /* copy file to backup directory */
721  mrmailbox_log_info(mailbox, 0, "Backup \"%s\" to \"%s\".", mailbox->m_dbfile, dest_pathNfilename);
722  if( !mr_copy_file(mailbox->m_dbfile, dest_pathNfilename, mailbox) ) {
723  goto cleanup; /* error already logged */
724  }
725 
726  /* unlock and re-open the source and make it availabe again for the normal use */
727  mrsqlite3_open__(mailbox->m_sql, mailbox->m_dbfile, 0);
728  closed = 0;
729  mrsqlite3_unlock(mailbox->m_sql);
730  locked = 0;
731 
732  /* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
733  if( (dest_sql=mrsqlite3_new(mailbox/*for logging only*/))==NULL
734  || !mrsqlite3_open__(dest_sql, dest_pathNfilename, 0) ) {
735  goto cleanup; /* error already logged */
736  }
737 
738  if( !mrsqlite3_table_exists__(dest_sql, "backup_blobs") ) {
739  if( !mrsqlite3_execute__(dest_sql, "CREATE TABLE backup_blobs (id INTEGER PRIMARY KEY, file_name, file_content);") ) {
740  goto cleanup; /* error already logged */
741  }
742  }
743 
744  /* scan directory, pass 1: collect file info */
745  total_files_count = 0;
746  if( (dir_handle=opendir(mailbox->m_blobdir))==NULL ) {
747  mrmailbox_log_error(mailbox, 0, "Backup: Cannot get info for blob-directory \"%s\".", mailbox->m_blobdir);
748  goto cleanup;
749  }
750 
751  while( (dir_entry=readdir(dir_handle))!=NULL ) {
752  total_files_count++;
753  }
754 
755  closedir(dir_handle);
756  dir_handle = NULL;
757 
758  if( total_files_count>0 )
759  {
760  /* scan directory, pass 2: copy files */
761  if( (dir_handle=opendir(mailbox->m_blobdir))==NULL ) {
762  mrmailbox_log_error(mailbox, 0, "Backup: Cannot copy from blob-directory \"%s\".", mailbox->m_blobdir);
763  goto cleanup;
764  }
765 
766  stmt = mrsqlite3_prepare_v2_(dest_sql, "INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);");
767  while( (dir_entry=readdir(dir_handle))!=NULL )
768  {
769  if( s_imex_do_exit ) {
770  delete_dest_file = 1;
771  goto cleanup;
772  }
773 
774  FILE_PROGRESS
775 
776  char* name = dir_entry->d_name; /* name without path; may also be `.` or `..` */
777  int name_len = strlen(name);
778  if( (name_len==1 && name[0]=='.')
779  || (name_len==2 && name[0]=='.' && name[1]=='.')
780  || (name_len > prefix_len && strncmp(name, MR_BAK_PREFIX, prefix_len)==0 && name_len > suffix_len && strncmp(&name[name_len-suffix_len-1], "." MR_BAK_SUFFIX, suffix_len)==0) ) {
781  //mrmailbox_log_info(mailbox, 0, "Backup: Skipping \"%s\".", name);
782  continue;
783  }
784 
785  //mrmailbox_log_info(mailbox, 0, "Backup \"%s\".", name);
786  free(curr_pathNfilename);
787  curr_pathNfilename = mr_mprintf("%s/%s", mailbox->m_blobdir, name);
788  free(buf);
789  if( !mr_read_file(curr_pathNfilename, &buf, &buf_bytes, mailbox) || buf==NULL || buf_bytes<=0 ) {
790  continue;
791  }
792 
793  sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
794  sqlite3_bind_blob(stmt, 2, buf, buf_bytes, SQLITE_STATIC);
795  if( sqlite3_step(stmt)!=SQLITE_DONE ) {
796  mrmailbox_log_error(mailbox, 0, "Disk full? Cannot add file \"%s\" to backup.", curr_pathNfilename);
797  goto cleanup; /* this is not recoverable! writing to the sqlite database should work! */
798  }
799  sqlite3_reset(stmt);
800  }
801  }
802  else
803  {
804  mrmailbox_log_info(mailbox, 0, "Backup: No files to copy.", mailbox->m_blobdir);
805  }
806 
807  /* done - set some special config values (do this last to avoid importing crashed backups) */
808  mrsqlite3_set_config_int__(dest_sql, "backup_time", now);
809  mrsqlite3_set_config__ (dest_sql, "backup_for", mailbox->m_blobdir);
810 
811  mailbox->m_cb(mailbox, MR_EVENT_IMEX_FILE_WRITTEN, (uintptr_t)dest_pathNfilename, (uintptr_t)"application/octet-stream");
812  success = 1;
813 
814 cleanup:
815  if( dir_handle ) { closedir(dir_handle); }
816  if( closed ) { mrsqlite3_open__(mailbox->m_sql, mailbox->m_dbfile, 0); }
817  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
818 
819  if( stmt ) { sqlite3_finalize(stmt); }
820  mrsqlite3_close__(dest_sql);
821  mrsqlite3_unref(dest_sql);
822  if( delete_dest_file ) { mr_delete_file(dest_pathNfilename, mailbox); }
823  free(dest_pathNfilename);
824 
825  free(curr_pathNfilename);
826  free(buf);
827  return success;
828 }
829 
830 
831 /*******************************************************************************
832  * Import backup
833  ******************************************************************************/
834 
835 
849 char* mrmailbox_imex_has_backup(mrmailbox_t* mailbox, const char* dir_name)
850 {
851  char* ret = NULL;
852  time_t ret_backup_time = 0;
853  DIR* dir_handle = NULL;
854  struct dirent* dir_entry;
855  int prefix_len = strlen(MR_BAK_PREFIX);
856  int suffix_len = strlen(MR_BAK_SUFFIX);
857  char* curr_pathNfilename = NULL;
858  mrsqlite3_t* test_sql = NULL;
859 
860  if( mailbox == NULL ) {
861  return NULL;
862  }
863 
864  if( (dir_handle=opendir(dir_name))==NULL ) {
865  mrmailbox_log_info(mailbox, 0, "Backup check: Cannot open directory \"%s\".", dir_name); /* this is not an error - eg. the directory may not exist or the user has not given us access to read data from the storage */
866  goto cleanup;
867  }
868 
869  while( (dir_entry=readdir(dir_handle))!=NULL ) {
870  const char* name = dir_entry->d_name; /* name without path; may also be `.` or `..` */
871  int name_len = strlen(name);
872  if( name_len > prefix_len && strncmp(name, MR_BAK_PREFIX, prefix_len)==0
873  && name_len > suffix_len && strncmp(&name[name_len-suffix_len-1], "." MR_BAK_SUFFIX, suffix_len)==0 )
874  {
875  free(curr_pathNfilename);
876  curr_pathNfilename = mr_mprintf("%s/%s", dir_name, name);
877 
878  mrsqlite3_unref(test_sql);
879  if( (test_sql=mrsqlite3_new(mailbox/*for logging only*/))!=NULL
880  && mrsqlite3_open__(test_sql, curr_pathNfilename, MR_OPEN_READONLY) )
881  {
882  time_t curr_backup_time = mrsqlite3_get_config_int__(test_sql, "backup_time", 0); /* reading the backup time also checks if the database is readable and the table `config` exists */
883  if( curr_backup_time > 0
884  && curr_backup_time > ret_backup_time/*use the newest if there are multiple backup*/ )
885  {
886  /* set return value to the tested database name */
887  free(ret);
888  ret = curr_pathNfilename;
889  ret_backup_time = curr_backup_time;
890  curr_pathNfilename = NULL;
891  }
892  }
893  }
894  }
895 
896 cleanup:
897  if( dir_handle ) { closedir(dir_handle); }
898  free(curr_pathNfilename);
899  mrsqlite3_unref(test_sql);
900  return ret;
901 }
902 
903 
904 static int import_backup(mrmailbox_t* mailbox, const char* backup_to_import)
905 {
906  /* command for testing eg.
907  imex import-backup /home/bpetersen/temp/delta-chat-2017-10-05.bak
908  */
909 
910  int success = 0;
911  int locked = 0;
912  int processed_files_count = 0, total_files_count = 0;
913  sqlite3_stmt* stmt = NULL;
914  char* pathNfilename = NULL;
915 
916  mrmailbox_log_info(mailbox, 0, "Import \"%s\" to \"%s\".", backup_to_import, mailbox->m_dbfile);
917 
918  if( mrmailbox_is_configured(mailbox) ) {
919  mrmailbox_log_error(mailbox, 0, "Cannot import backups to mailboxes in use.");
920  goto cleanup;
921  }
922 
923  /* close and delete the original file */
924  mrmailbox_disconnect(mailbox);
925 
926  mrsqlite3_lock(mailbox->m_sql);
927  locked = 1;
928 
929  if( mrsqlite3_is_open(mailbox->m_sql) ) {
930  mrsqlite3_close__(mailbox->m_sql);
931  }
932 
933  mr_delete_file(mailbox->m_dbfile, mailbox);
934 
935  if( mr_file_exist(mailbox->m_dbfile) ) {
936  mrmailbox_log_error(mailbox, 0, "Cannot import backups: Cannot delete the old file.");
937  goto cleanup;
938  }
939 
940  /* copy the database file */
941  if( !mr_copy_file(backup_to_import, mailbox->m_dbfile, mailbox) ) {
942  goto cleanup; /* error already logged */
943  }
944 
945  /* re-open copied database file */
946  if( !mrsqlite3_open__(mailbox->m_sql, mailbox->m_dbfile, 0) ) {
947  goto cleanup;
948  }
949 
950  /* copy all blobs to files */
951  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "SELECT COUNT(*) FROM backup_blobs;");
952  sqlite3_step(stmt);
953  total_files_count = sqlite3_column_int(stmt, 0);
954  sqlite3_finalize(stmt);
955  stmt = NULL;
956 
957  stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "SELECT file_name, file_content FROM backup_blobs ORDER BY id;");
958  while( sqlite3_step(stmt) == SQLITE_ROW )
959  {
960  if( s_imex_do_exit ) {
961  goto cleanup;
962  }
963 
964  FILE_PROGRESS
965 
966  const char* file_name = (const char*)sqlite3_column_text (stmt, 0);
967  int file_bytes = sqlite3_column_bytes(stmt, 1);
968  const void* file_content = sqlite3_column_blob (stmt, 1);
969 
970  if( file_bytes > 0 && file_content ) {
971  free(pathNfilename);
972  pathNfilename = mr_mprintf("%s/%s", mailbox->m_blobdir, file_name);
973  if( !mr_write_file(pathNfilename, file_content, file_bytes, mailbox) ) {
974  mrmailbox_log_error(mailbox, 0, "Storage full? Cannot write file %s with %i bytes.", pathNfilename, file_bytes);
975  goto cleanup; /* otherwise the user may believe the stuff is imported correctly, but there are files missing ... */
976  }
977  }
978  }
979 
980  /* finalize/reset all statements - otherwise the table cannot be DROPped below */
981  sqlite3_finalize(stmt);
982  stmt = 0;
983  mrsqlite3_reset_all_predefinitions(mailbox->m_sql);
984 
985  mrsqlite3_execute__(mailbox->m_sql, "DROP TABLE backup_blobs;");
986  mrsqlite3_execute__(mailbox->m_sql, "VACUUM;");
987 
988  success = 1;
989 
990 cleanup:
991  free(pathNfilename);
992  if( stmt ) { sqlite3_finalize(stmt); }
993  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
994  return success;
995 }
996 
997 
998 /*******************************************************************************
999  * Import/Export Thread and Main Interface
1000  ******************************************************************************/
1001 
1002 
1003 typedef struct mrimexthreadparam_t
1004 {
1005  mrmailbox_t* m_mailbox;
1006  int m_what;
1007  char* m_param1; /* meaning depends on m_what */
1008  char* m_setup_code;
1009 } mrimexthreadparam_t;
1010 
1011 
1012 static pthread_t s_imex_thread;
1013 static int s_imex_thread_created = 0;
1014 
1015 
1016 static void* imex_thread_entry_point(void* entry_arg)
1017 {
1018  int success = 0;
1019  mrimexthreadparam_t* thread_param = (mrimexthreadparam_t*)entry_arg;
1020  mrmailbox_t* mailbox = thread_param->m_mailbox; /*keep a local pointer as we free thread_param sooner or later */
1021 
1022  mrosnative_setup_thread(mailbox); /* must be first */
1023  mrmailbox_log_info(mailbox, 0, "Import/export thread started.");
1024 
1025  if( !mrsqlite3_is_open(thread_param->m_mailbox->m_sql) ) {
1026  mrmailbox_log_error(mailbox, 0, "Import/export: Database not opened.");
1027  goto cleanup;
1028  }
1029 
1030  if( thread_param->m_what==MR_IMEX_EXPORT_SELF_KEYS || thread_param->m_what==MR_IMEX_EXPORT_BACKUP ) {
1031  /* before we export anything, make sure the private key exists */
1032  if( !mrmailbox_ensure_secret_key_exists(mailbox) ) {
1033  mrmailbox_log_error(mailbox, 0, "Import/export: Cannot create private key or private key not available.");
1034  goto cleanup;
1035  }
1036  /* also make sure, the directory for exporting exists */
1037  mr_create_folder(thread_param->m_param1, mailbox);
1038  }
1039 
1040  switch( thread_param->m_what )
1041  {
1042  case MR_IMEX_EXPORT_SELF_KEYS:
1043  if( !export_self_keys(mailbox, thread_param->m_param1) ) {
1044  goto cleanup;
1045  }
1046  break;
1047 
1048  case MR_IMEX_IMPORT_SELF_KEYS:
1049  if( !import_self_keys(mailbox, thread_param->m_param1) ) {
1050  goto cleanup;
1051  }
1052  break;
1053 
1054  case MR_IMEX_EXPORT_BACKUP:
1055  if( !export_backup(mailbox, thread_param->m_param1) ) {
1056  goto cleanup;
1057  }
1058  break;
1059 
1060  case MR_IMEX_IMPORT_BACKUP:
1061  if( !import_backup(mailbox, thread_param->m_param1) ) {
1062  goto cleanup;
1063  }
1064  break;
1065 
1066  case MR_IMEX_EXPORT_SETUP_MESSAGE:
1067  if( !export_setup_file(mailbox, thread_param->m_param1, thread_param->m_setup_code) ) {
1068  goto cleanup;
1069  }
1070  break;
1071  }
1072 
1073  success = 1;
1074 
1075 cleanup:
1076  mrmailbox_log_info(mailbox, 0, "Import/export thread ended.");
1077  s_imex_do_exit = 1; /* set this before sending MR_EVENT_EXPORT_ENDED, avoids MR_IMEX_CANCEL to stop the thread */
1078  mailbox->m_cb(mailbox, MR_EVENT_IMEX_ENDED, success, 0);
1079  s_imex_thread_created = 0;
1080  free(thread_param->m_param1);
1081  free(thread_param->m_setup_code);
1082  free(thread_param);
1083  mrosnative_unsetup_thread(mailbox); /* must be very last (here we really new the local copy of the pointer) */
1084  return NULL;
1085 }
1086 
1087 
1108 void mrmailbox_imex(mrmailbox_t* mailbox, int what, const char* param1, const char* setup_code)
1109 {
1110  mrimexthreadparam_t* thread_param;
1111 
1112  if( mailbox==NULL || mailbox->m_sql==NULL ) {
1113  return;
1114  }
1115 
1116  if( what == MR_IMEX_CANCEL ) {
1117  /* cancel an running export */
1118  if( s_imex_thread_created && s_imex_do_exit==0 ) {
1119  mrmailbox_log_info(mailbox, 0, "Stopping import/export thread...");
1120  s_imex_do_exit = 1;
1121  pthread_join(s_imex_thread, NULL);
1122  mrmailbox_log_info(mailbox, 0, "Import/export thread stopped.");
1123  }
1124  return;
1125  }
1126 
1127  if( param1 == NULL ) {
1128  mrmailbox_log_error(mailbox, 0, "No Import/export dir/file given.");
1129  return;
1130  }
1131 
1132  if( s_imex_thread_created || s_imex_do_exit==0 ) {
1133  mrmailbox_log_warning(mailbox, 0, "Already importing/exporting.");
1134  return;
1135  }
1136  s_imex_thread_created = 1;
1137  s_imex_do_exit = 0;
1138 
1139  memset(&s_imex_thread, 0, sizeof(pthread_t));
1140  thread_param = calloc(1, sizeof(mrimexthreadparam_t));
1141  thread_param->m_mailbox = mailbox;
1142  thread_param->m_what = what;
1143  thread_param->m_param1 = safe_strdup(param1);
1144  thread_param->m_setup_code = safe_strdup(setup_code);
1145  pthread_create(&s_imex_thread, NULL, imex_thread_entry_point, thread_param);
1146 }
1147 
1148 
1161 int mrmailbox_check_password(mrmailbox_t* mailbox, const char* test_pw)
1162 {
1163  /* Check if the given password matches the configured mail_pw.
1164  This is to prompt the user before starting eg. an export; this is mainly to avoid doing people bad thinkgs if they have short access to the device.
1165  When we start supporting OAuth some day, we should think this over, maybe force the user to re-authenticate hinself with the Android password. */
1166  mrloginparam_t* loginparam = mrloginparam_new();
1167  int success = 0;
1168 
1169  if( mailbox==NULL ) {
1170  goto cleanup;
1171  }
1172 
1173  mrsqlite3_lock(mailbox->m_sql);
1174 
1175  mrloginparam_read__(loginparam, mailbox->m_sql, "configured_");
1176 
1177  mrsqlite3_unlock(mailbox->m_sql);
1178 
1179  if( (loginparam->m_mail_pw==NULL || loginparam->m_mail_pw[0]==0) && (test_pw==NULL || test_pw[0]==0) ) {
1180  /* both empty or unset */
1181  success = 1;
1182  }
1183  else if( loginparam->m_mail_pw==NULL || test_pw==NULL ) {
1184  /* one set, the other not */
1185  success = 0;
1186  }
1187  else if( strcmp(loginparam->m_mail_pw, test_pw)==0 ) {
1188  /* string-compared passwords are equal */
1189  success = 1;
1190  }
1191 
1192 cleanup:
1193  mrloginparam_unref(loginparam);
1194  return success;
1195 }
1196 
1197 
1219 {
1220  #define CODE_ELEMS 9
1221  #define BUF_BYTES (CODE_ELEMS*sizeof(uint16_t))
1222  uint16_t buf[CODE_ELEMS];
1223  int i;
1224 
1225  if( !RAND_bytes((unsigned char*)buf, BUF_BYTES) ) {
1226  mrmailbox_log_warning(mailbox, 0, "Falling back to pseudo-number generation for the setup code.");
1227  RAND_pseudo_bytes((unsigned char*)buf, BUF_BYTES);
1228  }
1229 
1230  for( i = 0; i < CODE_ELEMS; i++ ) {
1231  buf[i] = buf[i] % 10000; /* force all blocks into the range 0..9999 */
1232  }
1233 
1234  return mr_mprintf("%04i-%04i-%04i-"
1235  "%04i-%04i-%04i-"
1236  "%04i-%04i-%04i",
1237  (int)buf[0], (int)buf[1], (int)buf[2],
1238  (int)buf[3], (int)buf[4], (int)buf[5],
1239  (int)buf[6], (int)buf[7], (int)buf[8]);
1240 }
char * m_dbfile
the database file in file.
Definition: mrmailbox.h:144
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrmailbox_disconnect(mrmailbox_t *mailbox)
Disonnect the mailbox from the server.
Definition: mrmailbox.c:1605
-
char * m_blobdir
full path of the blob directory in use.
Definition: mrmailbox.h:145
-
int mrmailbox_is_configured(mrmailbox_t *mailbox)
Check if the mailbox is already configured.
-
char * mrmailbox_imex_has_backup(mrmailbox_t *mailbox, const char *dir_name)
Check if there is a backup file.
-
int mrmailbox_check_password(mrmailbox_t *mailbox, const char *test_pw)
Check if the user is authorized by the given password in some way.
-
char * mrmailbox_create_setup_code(mrmailbox_t *mailbox)
Create random setup code.
-
void mrmailbox_imex(mrmailbox_t *mailbox, int what, const char *param1, const char *setup_code)
Import/export things.
-
- - - - diff --git a/docs/user/html/mrmailbox__log_8c_source.html b/docs/user/html/mrmailbox__log_8c_source.html deleted file mode 100644 index 126cead8..00000000 --- a/docs/user/html/mrmailbox__log_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmailbox_log.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmailbox_log.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Asynchronous "Thread-errors" are reported by the mrmailbox_log_error()
24 function. These errors must be shown to the user by a bubble or so.
25 
26 "Normal" errors are usually returned by a special value (null or so) and are
27 usually not reported using mrmailbox_log_error() - its up to the caller to
28 decide, what should be reported or done. However, these "Normal" errors
29 are usually logged by mrmailbox_log_warning(). */
30 
31 
32 #include <stdarg.h>
33 #include <memory.h>
34 #include "mrmailbox_internal.h"
35 
36 
37 /*******************************************************************************
38  * Get a unique thread ID to recognize log output from different threads
39  ******************************************************************************/
40 
41 
42 int mrmailbox_get_thread_index(void)
43 {
44  #define MR_MAX_THREADS 32 /* if more threads are started, the full ID is printed (this may happen eg. on many failed connections so that we try to start a working thread several times) */
45  static pthread_t s_threadIds[MR_MAX_THREADS];
46  static int s_threadIdsCnt = 0;
47 
48  int i;
49  pthread_t self = pthread_self();
50 
51  if( s_threadIdsCnt==0 ) {
52  for( i = 0; i < MR_MAX_THREADS; i++ ) {
53  s_threadIds[i] = 0;
54  }
55  }
56 
57  for( i = 0; i < s_threadIdsCnt; i++ ) {
58  if( s_threadIds[i] == self ) {
59  return i+1;
60  }
61  }
62 
63  if( s_threadIdsCnt >= MR_MAX_THREADS ) {
64  return (int)(self); /* Fallback, this may happen, see comment above */
65  }
66 
67  s_threadIds[s_threadIdsCnt] = self;
68  s_threadIdsCnt++;
69  return s_threadIdsCnt;
70 }
71 
72 
73 /*******************************************************************************
74  * Main interface
75  ******************************************************************************/
76 
77 
78 void mrmailbox_log_vprintf(mrmailbox_t* mailbox, int event, int code, const char* msg_format, va_list va)
79 {
80  char* msg = NULL;
81 
82  if( mailbox==NULL ) {
83  return;
84  }
85 
86  /* format message from variable parameters or translate very comming errors */
87  if( code == MR_ERR_SELF_NOT_IN_GROUP )
88  {
89  msg = mrstock_str(MR_STR_SELFNOTINGRP);
90  }
91  else if( code == MR_ERR_NONETWORK )
92  {
93  msg = mrstock_str(MR_STR_NONETWORK);
94  }
95  else if( msg_format )
96  {
97  #define BUFSIZE 1024
98  char tempbuf[BUFSIZE];
99  vsnprintf(tempbuf, BUFSIZE, msg_format, va);
100  msg = safe_strdup(tempbuf);
101 
102  if( event == MR_EVENT_ERROR ) {
103  char* temp = msg;
104  msg = mrstock_str_repl_string(MR_STR_ERROR, temp);
105  free(temp);
106  }
107  }
108 
109  /* if we have still no message, create one based upon the code */
110  if( msg == NULL ) {
111  if( event == MR_EVENT_INFO ) { msg = mr_mprintf("Info: %i", (int)code); }
112  else if( event == MR_EVENT_WARNING ) { msg = mr_mprintf("Warning: %i", (int)code); }
113  else { msg = mrstock_str_repl_int(MR_STR_ERROR, code); }
114  }
115 
116  /* prefix the message by the thread-id? we do this for non-errros that are normally only logged (for the few errros, the thread should be clear (enough)) */
117  if( event != MR_EVENT_ERROR ) {
118  char* temp = msg;
119  msg = mr_mprintf("T%i: %s", (int)mrmailbox_get_thread_index(), temp);
120  free(temp);
121  }
122 
123  /* finally, log */
124  mailbox->m_cb(mailbox, event, (uintptr_t)code, (uintptr_t)msg);
125 
126  /* remember the last N log entries */
127  pthread_mutex_lock(&mailbox->m_log_ringbuf_critical);
128  free(mailbox->m_log_ringbuf[mailbox->m_log_ringbuf_pos]);
129  mailbox->m_log_ringbuf[mailbox->m_log_ringbuf_pos] = msg;
130  mailbox->m_log_ringbuf_times[mailbox->m_log_ringbuf_pos] = time(NULL);
131  mailbox->m_log_ringbuf_pos = (mailbox->m_log_ringbuf_pos+1) % MR_LOG_RINGBUF_SIZE;
132  pthread_mutex_unlock(&mailbox->m_log_ringbuf_critical);
133 }
134 
135 
136 void mrmailbox_log_info(mrmailbox_t* mailbox, int code, const char* msg, ...)
137 {
138  va_list va;
139  va_start(va, msg); /* va_start() expects the last non-variable argument as the second parameter */
140  mrmailbox_log_vprintf(mailbox, MR_EVENT_INFO, code, msg, va);
141  va_end(va);
142 }
143 
144 
145 
146 void mrmailbox_log_warning(mrmailbox_t* mailbox, int code, const char* msg, ...)
147 {
148  va_list va;
149  va_start(va, msg);
150  mrmailbox_log_vprintf(mailbox, MR_EVENT_WARNING, code, msg, va);
151  va_end(va);
152 }
153 
154 
155 void mrmailbox_log_error(mrmailbox_t* mailbox, int code, const char* msg, ...)
156 {
157  va_list va;
158  va_start(va, msg);
159  mrmailbox_log_vprintf(mailbox, MR_EVENT_ERROR, code, msg, va);
160  va_end(va);
161 }
162 
163 
164 void mrmailbox_log_error_if(int* condition, mrmailbox_t* mailbox, int code, const char* msg, ...)
165 {
166  if( condition == NULL || mailbox==NULL ) {
167  return;
168  }
169 
170  va_list va;
171  va_start(va, msg);
172  if( *condition ) {
173  /* pop-up error, if we're offline, force a "not connected" error (the function is not used for other cases) */
174  if( mailbox->m_cb(mailbox, MR_EVENT_IS_ONLINE, 0, 0)!=1 ) {
175  mrmailbox_log_vprintf(mailbox, MR_EVENT_ERROR, MR_ERR_NONETWORK, NULL, va);
176  }
177  else {
178  mrmailbox_log_vprintf(mailbox, MR_EVENT_ERROR, code, msg, va);
179  }
180  *condition = 0;
181  }
182  else {
183  /* log a warning only (eg. for subsequent connection errors) */
184  mrmailbox_log_vprintf(mailbox, MR_EVENT_WARNING, code, msg, va);
185  }
186  va_end(va);
187 }
188 
189 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrmailbox__tools_8c_source.html b/docs/user/html/mrmailbox__tools_8c_source.html deleted file mode 100644 index cd11eb89..00000000 --- a/docs/user/html/mrmailbox__tools_8c_source.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmailbox_tools.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmailbox_tools.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrmimeparser.h"
25 
26 
27 /*******************************************************************************
28  * Add contacts to database on receiving messages
29  ******************************************************************************/
30 
31 
32 static void add_or_lookup_contact_by_addr__(mrmailbox_t* ths, const char* display_name_enc, const char* addr_spec, int origin, carray* ids, int* check_self)
33 {
34  /* is addr_spec equal to SELF? */
35  int dummy;
36  if( check_self == NULL ) { check_self = &dummy; }
37 
38  *check_self = 0;
39 
40  char* self_addr = mrsqlite3_get_config__(ths->m_sql, "configured_addr", "");
41  if( strcasecmp(self_addr, addr_spec)==0 ) {
42  *check_self = 1;
43  }
44  free(self_addr);
45 
46  if( *check_self ) {
47  return;
48  }
49 
50  /* add addr_spec if missing, update otherwise */
51  char* display_name_dec = NULL;
52  if( display_name_enc ) {
53  display_name_dec = mr_decode_header_string(display_name_enc);
54  mrcontact_normalize_name(display_name_dec);
55  }
56 
57  uint32_t row_id = mrmailbox_add_or_lookup_contact__(ths, display_name_dec /*can be NULL*/, addr_spec, origin, NULL);
58 
59  free(display_name_dec);
60 
61  if( row_id ) {
62  if( !carray_search(ids, (void*)(uintptr_t)row_id, NULL) ) {
63  carray_add(ids, (void*)(uintptr_t)row_id, NULL);
64  }
65  }
66 }
67 
68 
69 void mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mrmailbox_t* ths, struct mailimf_mailbox_list* mb_list, int origin, carray* ids, int* check_self)
70 {
71  clistiter* cur;
72  for( cur = clist_begin(mb_list->mb_list); cur!=NULL ; cur=clist_next(cur) ) {
73  struct mailimf_mailbox* mb = (struct mailimf_mailbox*)clist_content(cur);
74  if( mb ) {
75  add_or_lookup_contact_by_addr__(ths, mb->mb_display_name, mb->mb_addr_spec, origin, ids, check_self);
76  }
77  }
78 }
79 
80 
81 void mrmailbox_add_or_lookup_contacts_by_address_list__(mrmailbox_t* ths, struct mailimf_address_list* adr_list, int origin, carray* ids, int* check_self)
82 {
83  clistiter* cur;
84  for( cur = clist_begin(adr_list->ad_list); cur!=NULL ; cur=clist_next(cur) ) {
85  struct mailimf_address* adr = (struct mailimf_address*)clist_content(cur);
86  if( adr ) {
87  if( adr->ad_type == MAILIMF_ADDRESS_MAILBOX ) {
88  struct mailimf_mailbox* mb = adr->ad_data.ad_mailbox; /* can be NULL */
89  if( mb ) {
90  add_or_lookup_contact_by_addr__(ths, mb->mb_display_name, mb->mb_addr_spec, origin, ids, check_self);
91  }
92  }
93  else if( adr->ad_type == MAILIMF_ADDRESS_GROUP ) {
94  struct mailimf_group* group = adr->ad_data.ad_group; /* can be NULL */
95  if( group && group->grp_mb_list /*can be NULL*/ ) {
96  mrmailbox_add_or_lookup_contacts_by_mailbox_list__(ths, group->grp_mb_list, origin, ids, check_self);
97  }
98  }
99  }
100  }
101 }
102 
103 
104 /*******************************************************************************
105  * Check if a message is a reply to a known message (messenger or non-messenger)
106  ******************************************************************************/
107 
108 
109 static int is_known_rfc724_mid__(mrmailbox_t* mailbox, const char* rfc724_mid)
110 {
111  if( rfc724_mid ) {
112  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_cm,
113  "SELECT id FROM msgs "
114  " WHERE rfc724_mid=? "
115  " AND chat_id!=" MR_STRINGIFY(MR_CHAT_ID_TRASH) /*eg. do not replies to our mailinglist messages as known*/
116  " AND (chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) " OR from_id=" MR_STRINGIFY(MR_CONTACT_ID_SELF) ");");
117  sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
118  if( sqlite3_step(stmt) == SQLITE_ROW ) {
119  return 1;
120  }
121  }
122  return 0;
123 }
124 
125 
126 static int is_known_rfc724_mid_in_list__(mrmailbox_t* mailbox, const clist* mid_list)
127 {
128  if( mid_list ) {
129  clistiter* cur;
130  for( cur = clist_begin(mid_list); cur!=NULL ; cur=clist_next(cur) ) {
131  if( is_known_rfc724_mid__(mailbox, clist_content(cur)) ) {
132  return 1;
133  }
134  }
135  }
136 
137  return 0;
138 }
139 
140 
141 int mrmailbox_is_reply_to_known_message__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser)
142 {
143  /* check if the message is a reply to a known message; the replies are identified by the Message-ID from
144  `In-Reply-To`/`References:` (to support non-Delta-Clients) or from `X-MrPredecessor:` (Delta clients, see comment in mrchat.c) */
145  clistiter* cur;
146  for( cur = clist_begin(mime_parser->m_header->fld_list); cur!=NULL ; cur=clist_next(cur) )
147  {
148  struct mailimf_field* field = (struct mailimf_field*)clist_content(cur);
149  if( field )
150  {
151  if( field->fld_type == MAILIMF_FIELD_OPTIONAL_FIELD )
152  {
153  struct mailimf_optional_field* optional_field = field->fld_data.fld_optional_field;
154  if( optional_field && optional_field->fld_name ) {
155  if( strcasecmp(optional_field->fld_name, "X-MrPredecessor")==0 || strcasecmp(optional_field->fld_name, "Chat-Predecessor")==0 ) { /* see comment in mrchat.c */
156  if( is_known_rfc724_mid__(mailbox, optional_field->fld_value) ) {
157  return 1;
158  }
159  }
160  }
161  }
162  else if( field->fld_type == MAILIMF_FIELD_IN_REPLY_TO )
163  {
164  struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
165  if( fld_in_reply_to ) {
166  if( is_known_rfc724_mid_in_list__(mailbox, field->fld_data.fld_in_reply_to->mid_list) ) {
167  return 1;
168  }
169  }
170  }
171  else if( field->fld_type == MAILIMF_FIELD_REFERENCES )
172  {
173  struct mailimf_references* fld_references = field->fld_data.fld_references;
174  if( fld_references ) {
175  if( is_known_rfc724_mid_in_list__(mailbox, field->fld_data.fld_references->mid_list) ) {
176  return 1;
177  }
178  }
179  }
180 
181  }
182  }
183 
184  return 0;
185 }
186 
187 
188 /*******************************************************************************
189  * Check if a message is a reply to any messenger message
190  ******************************************************************************/
191 
192 
193 static int is_msgrmsg_rfc724_mid__(mrmailbox_t* mailbox, const char* rfc724_mid)
194 {
195  if( rfc724_mid ) {
196  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_mcm,
197  "SELECT id FROM msgs "
198  " WHERE rfc724_mid=? "
199  " AND msgrmsg!=0 "
200  " AND chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) ";");
201  sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
202  if( sqlite3_step(stmt) == SQLITE_ROW ) {
203  return 1;
204  }
205  }
206  return 0;
207 }
208 
209 
210 static int is_msgrmsg_rfc724_mid_in_list__(mrmailbox_t* mailbox, const clist* mid_list)
211 {
212  if( mid_list ) {
213  clistiter* cur;
214  for( cur = clist_begin(mid_list); cur!=NULL ; cur=clist_next(cur) ) {
215  if( is_msgrmsg_rfc724_mid__(mailbox, clist_content(cur)) ) {
216  return 1;
217  }
218  }
219  }
220 
221  return 0;
222 }
223 
224 
225 int mrmailbox_is_reply_to_messenger_message__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser)
226 {
227  /* function checks, if the message defined by mime_parser references a message send by us from Delta Chat.
228 
229  This is similar to is_reply_to_known_message__() but
230  - checks also if any of the referenced IDs are send by a messenger
231  - it is okay, if the referenced messages are moved to trash here
232  - no check for the Chat-* headers (function is only called if it is no messenger message itself) */
233  clistiter* cur;
234  for( cur = clist_begin(mime_parser->m_header->fld_list); cur!=NULL ; cur=clist_next(cur) )
235  {
236  struct mailimf_field* field = (struct mailimf_field*)clist_content(cur);
237  if( field )
238  {
239  if( field->fld_type == MAILIMF_FIELD_IN_REPLY_TO )
240  {
241  struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
242  if( fld_in_reply_to ) {
243  if( is_msgrmsg_rfc724_mid_in_list__(mailbox, field->fld_data.fld_in_reply_to->mid_list) ) {
244  return 1;
245  }
246  }
247  }
248  else if( field->fld_type == MAILIMF_FIELD_REFERENCES )
249  {
250  struct mailimf_references* fld_references = field->fld_data.fld_references;
251  if( fld_references ) {
252  if( is_msgrmsg_rfc724_mid_in_list__(mailbox, field->fld_data.fld_references->mid_list) ) {
253  return 1;
254  }
255  }
256  }
257 
258  }
259  }
260  return 0;
261 }
262 
263 
264 /*******************************************************************************
265  * Misc.
266  ******************************************************************************/
267 
268 
269 time_t mrmailbox_correct_bad_timestamp__(mrmailbox_t* ths, uint32_t chat_id, uint32_t from_id, time_t desired_timestamp, int is_fresh_msg)
270 {
271  /* used for correcting timestamps of _received_ messages.
272  use the last message from another user (including SELF) as the MINIMUM
273  (we do this check only for fresh messages, other messages may pop up whereever, this may happen eg. when restoring old messages or synchronizing different clients) */
274  if( is_fresh_msg )
275  {
276  sqlite3_stmt* stmt = mrsqlite3_predefine__(ths->m_sql, SELECT_timestamp_FROM_msgs_WHERE_timestamp,
277  "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?");
278  sqlite3_bind_int (stmt, 1, chat_id);
279  sqlite3_bind_int (stmt, 2, from_id);
280  sqlite3_bind_int64(stmt, 3, desired_timestamp);
281  if( sqlite3_step(stmt)==SQLITE_ROW )
282  {
283  time_t last_msg_time = sqlite3_column_int64(stmt, 0);
284  if( last_msg_time > 0 /* may happen as we do not check against sqlite3_column_type()!=SQLITE_NULL */ ) {
285  if( desired_timestamp <= last_msg_time ) {
286  desired_timestamp = last_msg_time+1; /* this may result in several incoming messages having the same
287  one-second-after-the-last-other-message-timestamp. however, this is no big deal
288  as we do not try to recrete the order of bad-date-messages and as we always order by ID as second criterion */
289  }
290  }
291  }
292  }
293 
294  /* use the (smeared) current time as the MAXIMUM */
295  if( desired_timestamp >= mr_smeared_time__() )
296  {
297  desired_timestamp = mr_create_smeared_timestamp__();
298  }
299 
300  return desired_timestamp;
301 }
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrcontact_normalize_name(char *full_name)
Normalize a name in-place.
Definition: mrcontact.c:130
-
- - - - diff --git a/docs/user/html/mrmimefactory_8c_source.html b/docs/user/html/mrmimefactory_8c_source.html deleted file mode 100644 index a5e220c2..00000000 --- a/docs/user/html/mrmimefactory_8c_source.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmimefactory.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmimefactory.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrmimefactory.h"
25 
26 #define LINEEND "\r\n" /* lineend used in IMF */
27 
28 
29 
30 /*******************************************************************************
31  * Load data
32  ******************************************************************************/
33 
34 
35 void mrmimefactory_init(mrmimefactory_t* factory, mrmailbox_t* mailbox)
36 {
37  if( factory == NULL || mailbox == NULL ) {
38  return;
39  }
40 
41  memset(factory, 0, sizeof(mrmimefactory_t));
42  factory->m_mailbox = mailbox;
43 }
44 
45 
46 void mrmimefactory_empty(mrmimefactory_t* factory)
47 {
48  if( factory == NULL ) {
49  return;
50  }
51 
52  free(factory->m_from_addr);
53  factory->m_from_addr = NULL;
54 
55  free(factory->m_from_displayname);
56  factory->m_from_displayname = NULL;
57 
58  free(factory->m_selfstatus);
59  factory->m_selfstatus = NULL;
60 
61  if( factory->m_recipients_names ) {
62  clist_free_content(factory->m_recipients_names);
63  clist_free(factory->m_recipients_names);
64  factory->m_recipients_names = NULL;
65  }
66 
67  if( factory->m_recipients_addr ) {
68  clist_free_content(factory->m_recipients_addr);
69  clist_free(factory->m_recipients_addr);
70  factory->m_recipients_addr = NULL;
71  }
72 
73  mrmsg_unref(factory->m_msg);
74  factory->m_msg = NULL;
75 
76  mrchat_unref(factory->m_chat);
77  factory->m_chat = NULL;
78 
79  if( factory->m_out ) {
80  mmap_string_free(factory->m_out);
81  factory->m_out = NULL;
82  }
83  factory->m_out_encrypted = 0;
84  factory->m_loaded = MR_MF_NOTHING_LOADED;
85 
86  factory->m_timestamp = 0;
87 }
88 
89 
90 static void load_from__(mrmimefactory_t* factory)
91 {
92  factory->m_from_addr = mrsqlite3_get_config__(factory->m_mailbox->m_sql, "configured_addr", NULL);
93  factory->m_from_displayname = mrsqlite3_get_config__(factory->m_mailbox->m_sql, "displayname", NULL);
94 
95  factory->m_selfstatus = mrsqlite3_get_config__(factory->m_mailbox->m_sql, "selfstatus", NULL);
96  if( factory->m_selfstatus == NULL ) {
97  factory->m_selfstatus = mrstock_str(MR_STR_STATUSLINE);
98  }
99 }
100 
101 
102 int mrmimefactory_load_msg(mrmimefactory_t* factory, uint32_t msg_id)
103 {
104  int success = 0, locked = 0;
105 
106  if( factory == NULL || msg_id <= MR_MSG_ID_LAST_SPECIAL
107  || factory->m_mailbox == NULL
108  || factory->m_msg /*call empty() before */ ) {
109  goto cleanup;
110  }
111 
112  mrmailbox_t* mailbox = factory->m_mailbox;
113 
114  factory->m_recipients_names = clist_new();
115  factory->m_recipients_addr = clist_new();
116  factory->m_msg = mrmsg_new();
117  factory->m_chat = mrchat_new(mailbox);
118 
119  mrsqlite3_lock(mailbox->m_sql);
120  locked = 1;
121 
122  if( mrmsg_load_from_db__(factory->m_msg, mailbox, msg_id)
123  && mrchat_load_from_db__(factory->m_chat, factory->m_msg->m_chat_id) )
124  {
125  sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_na_FROM_chats_contacs_JOIN_contacts_WHERE_cc,
126  "SELECT c.authname, c.addr FROM chats_contacts cc LEFT JOIN contacts c ON cc.contact_id=c.id WHERE cc.chat_id=? AND cc.contact_id>?;");
127  sqlite3_bind_int(stmt, 1, factory->m_msg->m_chat_id);
128  sqlite3_bind_int(stmt, 2, MR_CONTACT_ID_LAST_SPECIAL);
129  while( sqlite3_step(stmt) == SQLITE_ROW )
130  {
131  const char* authname = (const char*)sqlite3_column_text(stmt, 0);
132  const char* addr = (const char*)sqlite3_column_text(stmt, 1);
133  if( clist_search_string_nocase(factory->m_recipients_addr, addr)==0 )
134  {
135  clist_append(factory->m_recipients_names, (void*)((authname&&authname[0])? safe_strdup(authname) : NULL));
136  clist_append(factory->m_recipients_addr, (void*)safe_strdup(addr));
137  }
138  }
139 
140  int system_command = mrparam_get_int(factory->m_msg->m_param, MRP_SYSTEM_CMD, 0);
141  if( system_command==MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP /* for added members, the list is just fine */) {
142  char* email_to_remove = mrparam_get(factory->m_msg->m_param, MRP_SYSTEM_CMD_PARAM, NULL);
143  char* self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", "");
144  if( email_to_remove && strcasecmp(email_to_remove, self_addr)!=0 )
145  {
146  if( clist_search_string_nocase(factory->m_recipients_addr, email_to_remove)==0 )
147  {
148  clist_append(factory->m_recipients_names, NULL);
149  clist_append(factory->m_recipients_addr, (void*)email_to_remove);
150  }
151  }
152  free(self_addr);
153  }
154 
155  load_from__(factory);
156 
157  factory->m_req_mdn = 0;
158  if( mrsqlite3_get_config_int__(mailbox->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED) ) {
159  factory->m_req_mdn = 1;
160  }
161 
162  /* Get a predecessor of the mail to send.
163  For simplicity, we use the last message send not by us.
164  This is not 100% accurate and may even be a newer message if first sending fails and new messages arrive -
165  however, as we currently only use it to identifify answers from different email addresses, this is sufficient.
166 
167  Our first idea was to write the predecessor to the `In-Reply-To:` header, however, this results
168  in infinite depth thread views eg. in thunderbird. Maybe we can work around this issue by using only one
169  predecessor anchor a day, however, for the moment, we just use the `X-MrPredecessor` header that does not
170  disturb other mailers.
171 
172  Finally, maybe the Predecessor/In-Reply-To header is not needed for all answers but only to the first ones -
173  or after the sender has changes its email address. */
174  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_rfc724_FROM_msgs_ORDER_BY_timestamp_LIMIT_1,
175  "SELECT rfc724_mid FROM msgs WHERE timestamp=(SELECT max(timestamp) FROM msgs WHERE chat_id=? AND from_id!=?);");
176  sqlite3_bind_int (stmt, 1, factory->m_msg->m_chat_id);
177  sqlite3_bind_int (stmt, 2, MR_CONTACT_ID_SELF);
178  if( sqlite3_step(stmt) == SQLITE_ROW ) {
179  factory->m_predecessor = strdup_keep_null((const char*)sqlite3_column_text(stmt, 0));
180  }
181 
182  /* get a References:-header: either the same as the last one or a random one.
183  To avoid endless nested threads, we do not use In-Reply-To: here but link subsequent mails to the same reference.
184  This "same reference" is re-calculated after 24 hours to avoid completely different messages being linked to an old context.
185 
186  Regarding multi-client: Different clients will create difference References:-header, maybe we will sync these headers some day,
187  however one could also see this as a feature :) (there may be different contextes on different clients)
188  (also, the References-header is not the most important thing, and, at least for now, we do not want to make things too complicated. */
189  time_t prev_msg_time = 0;
190  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_MAX_timestamp_FROM_msgs,
191  "SELECT max(timestamp) FROM msgs WHERE chat_id=? AND id!=?");
192  sqlite3_bind_int (stmt, 1, factory->m_msg->m_chat_id);
193  sqlite3_bind_int (stmt, 2, factory->m_msg->m_id);
194  if( sqlite3_step(stmt) == SQLITE_ROW ) {
195  prev_msg_time = sqlite3_column_int64(stmt, 0);
196  }
197 
198  #define NEW_THREAD_THRESHOLD 24*60*60
199  if( prev_msg_time != 0 && factory->m_msg->m_timestamp - prev_msg_time < NEW_THREAD_THRESHOLD ) {
200  factory->m_references = mrparam_get(factory->m_chat->m_param, MRP_REFERENCES, NULL);
201  }
202 
203  if( factory->m_references == NULL ) {
204  factory->m_references = mr_create_dummy_references_mid();
205  mrparam_set(factory->m_chat->m_param, MRP_REFERENCES, factory->m_references);
206  mrchat_update_param__(factory->m_chat);
207  }
208 
209  success = 1;
210  factory->m_loaded = MR_MF_MSG_LOADED;
211  factory->m_timestamp = factory->m_msg->m_timestamp;
212  factory->m_rfc724_mid = safe_strdup(factory->m_msg->m_rfc724_mid);
213  }
214 
215  if( success ) {
216  factory->m_increation = mrmsg_is_increation__(factory->m_msg);
217  }
218  mrsqlite3_unlock(mailbox->m_sql);
219  locked = 0;
220 
221 cleanup:
222  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
223  return success;
224 }
225 
226 
227 int mrmimefactory_load_mdn(mrmimefactory_t* factory, uint32_t msg_id)
228 {
229  int success = 0, locked = 0;
230  mrcontact_t* contact = mrcontact_new();
231 
232  if( factory == NULL ) {
233  goto cleanup;
234  }
235 
236  mrmailbox_t* mailbox = factory->m_mailbox;
237 
238  factory->m_recipients_names = clist_new();
239  factory->m_recipients_addr = clist_new();
240  factory->m_msg = mrmsg_new();
241 
242  mrsqlite3_lock(mailbox->m_sql);
243  locked = 1;
244 
245  if( !mrsqlite3_get_config_int__(mailbox->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED) ) {
246  goto cleanup; /* MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ... */
247  }
248 
249  if( !mrmsg_load_from_db__(factory->m_msg, mailbox, msg_id)
250  || !mrcontact_load_from_db__(contact, mailbox->m_sql, factory->m_msg->m_from_id) ) {
251  goto cleanup;
252  }
253 
254  if( contact->m_blocked
255  || factory->m_msg->m_chat_id<=MR_CHAT_ID_LAST_SPECIAL/* Do not send MDNs for contact requests, trash etc. */ ) {
256  goto cleanup;
257  }
258 
259  if( factory->m_msg->m_from_id <= MR_CONTACT_ID_LAST_SPECIAL ) {
260  goto cleanup;
261  }
262 
263  clist_append(factory->m_recipients_names, (void*)((contact->m_authname&&contact->m_authname[0])? safe_strdup(contact->m_authname) : NULL));
264  clist_append(factory->m_recipients_addr, (void*)safe_strdup(contact->m_addr));
265 
266  load_from__(factory);
267 
268  factory->m_timestamp = mr_create_smeared_timestamp__();
269  factory->m_rfc724_mid = mr_create_outgoing_rfc724_mid(NULL, factory->m_from_addr);
270 
271  mrsqlite3_unlock(mailbox->m_sql);
272  locked = 0;
273 
274  success = 1;
275  factory->m_loaded = MR_MF_MDN_LOADED;
276 
277 cleanup:
278  if( locked ) { mrsqlite3_unlock(mailbox->m_sql); }
279  return success;
280 }
281 
282 
283 /*******************************************************************************
284  * Render
285  ******************************************************************************/
286 
287 
288 static struct mailmime* build_body_text(char* text)
289 {
290  struct mailmime_fields* mime_fields;
291  struct mailmime* message_part;
292  struct mailmime_content* content;
293 
294  content = mailmime_content_new_with_str("text/plain");
295  clist_append(content->ct_parameters, mailmime_param_new_with_data("charset", "utf-8")); /* format=flowed currently does not really affect us, see https://www.ietf.org/rfc/rfc3676.txt */
296 
297  mime_fields = mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT);
298 
299  message_part = mailmime_new_empty(content, mime_fields);
300  mailmime_set_body_text(message_part, text, strlen(text));
301 
302  return message_part;
303 }
304 
305 
306 static struct mailmime* build_body_file(const mrmsg_t* msg, const char* base_name, char** ret_file_name_as_sended)
307 {
308  struct mailmime_fields* mime_fields;
309  struct mailmime* mime_sub = NULL;
310  struct mailmime_content* content;
311 
312  char* pathNfilename = mrparam_get(msg->m_param, MRP_FILE, NULL);
313  char* mimetype = mrparam_get(msg->m_param, MRP_MIMETYPE, NULL);
314  char* suffix = mr_get_filesuffix_lc(pathNfilename);
315  char* filename_to_send = NULL;
316 
317  if( pathNfilename == NULL ) {
318  goto cleanup;
319  }
320 
321  /* get file name to use for sending (for privacy purposes, we do not transfer the original filenames eg. for images; these names are normally not needed and contain timesamps, running numbers etc.) */
322  if( msg->m_type == MR_MSG_VOICE ) {
323  struct tm wanted_struct;
324  memcpy(&wanted_struct, localtime(&msg->m_timestamp), sizeof(struct tm));
325  filename_to_send = mr_mprintf("voice-message_%04i-%02i-%02i_%02i-%02i-%02i.%s",
326  (int)wanted_struct.tm_year+1900, (int)wanted_struct.tm_mon+1, (int)wanted_struct.tm_mday,
327  (int)wanted_struct.tm_hour, (int)wanted_struct.tm_min, (int)wanted_struct.tm_sec,
328  suffix? suffix : "dat");
329  }
330  else if( msg->m_type == MR_MSG_AUDIO ) {
331  char* author = mrparam_get(msg->m_param, MRP_AUTHORNAME, NULL);
332  char* title = mrparam_get(msg->m_param, MRP_TRACKNAME, NULL);
333  if( author && author[0] && title && title[0] && suffix ) {
334  filename_to_send = mr_mprintf("%s - %s.%s", author, title, suffix); /* the separator ` - ` is used on the receiver's side to construct the information; we avoid using ID3-scanners for security purposes */
335  }
336  else {
337  filename_to_send = mr_get_filename(pathNfilename);
338  }
339  free(author);
340  free(title);
341  }
342  else if( msg->m_type == MR_MSG_IMAGE || msg->m_type == MR_MSG_GIF ) {
343  if( base_name == NULL ) {
344  base_name = "image";
345  }
346  filename_to_send = mr_mprintf("%s.%s", base_name, suffix? suffix : "dat");
347  }
348  else if( msg->m_type == MR_MSG_VIDEO ) {
349  filename_to_send = mr_mprintf("video.%s", suffix? suffix : "dat");
350  }
351  else {
352  filename_to_send = mr_get_filename(pathNfilename);
353  }
354 
355  /* check mimetype */
356  if( mimetype == NULL && suffix != NULL ) {
357  if( strcmp(suffix, "png")==0 ) {
358  mimetype = safe_strdup("image/png");
359  }
360  else if( strcmp(suffix, "jpg")==0 || strcmp(suffix, "jpeg")==0 || strcmp(suffix, "jpe")==0 ) {
361  mimetype = safe_strdup("image/jpeg");
362  }
363  else if( strcmp(suffix, "gif")==0 ) {
364  mimetype = safe_strdup("image/gif");
365  }
366  else {
367  mimetype = safe_strdup("application/octet-stream");
368  }
369  }
370 
371  if( mimetype == NULL ) {
372  goto cleanup;
373  }
374 
375  /* create mime part, for Content-Disposition, see RFC 2183.
376  `Content-Disposition: attachment` seems not to make a difference to `Content-Disposition: inline` at least on tested Thunderbird and Gma'l in 2017.
377  But I've heard about problems with inline and outl'k, so we just use the attachment-type until we run into other problems ... */
378  mime_fields = mailmime_fields_new_filename(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
379  safe_strdup(filename_to_send), MAILMIME_MECHANISM_BASE64);
380 
381  if( ret_file_name_as_sended ) {
382  *ret_file_name_as_sended = safe_strdup(filename_to_send);
383  }
384 
385  content = mailmime_content_new_with_str(mimetype);
386 
387  mime_sub = mailmime_new_empty(content, mime_fields);
388 
389  mailmime_set_body_file(mime_sub, safe_strdup(pathNfilename));
390 
391 cleanup:
392  free(pathNfilename);
393  free(mimetype);
394  free(filename_to_send);
395  free(suffix);
396  return mime_sub;
397 }
398 
399 
400 static char* get_subject(const mrchat_t* chat, const mrmsg_t* msg, int afwd_email)
401 {
402  char *ret, *raw_subject = mrmsg_get_summarytext_by_raw(msg->m_type, msg->m_text, msg->m_param, APPROX_SUBJECT_CHARS);
403  const char* fwd = afwd_email? "Fwd: " : "";
404 
405  if( chat->m_type==MR_CHAT_TYPE_GROUP )
406  {
407  ret = mr_mprintf(MR_CHAT_PREFIX " %s: %s%s", chat->m_name, fwd, raw_subject);
408  }
409  else
410  {
411  ret = mr_mprintf(MR_CHAT_PREFIX " %s%s", fwd, raw_subject);
412  }
413 
414  free(raw_subject);
415  return ret;
416 }
417 
418 
419 int mrmimefactory_render(mrmimefactory_t* factory, int encrypt_to_self)
420 {
421  if( factory == NULL
422  || factory->m_loaded == MR_MF_NOTHING_LOADED
423  || factory->m_out/*call empty() before*/ ) {
424  return 0;
425  }
426 
427  struct mailimf_fields* imf_fields;
428  struct mailmime* message = NULL;
429  char* message_text = NULL, *message_text2 = NULL, *subject_str = NULL;
430  int afwd_email = 0;
431  int col = 0;
432  int success = 0;
433  int parts = 0;
434  mrmailbox_e2ee_helper_t e2ee_helper;
435  int e2ee_guaranteed = 0;
436  int system_command = 0;
437  int force_unencrypted = 0;
438  char* grpimage = NULL;
439 
440  memset(&e2ee_helper, 0, sizeof(mrmailbox_e2ee_helper_t));
441 
442 
443  /* create basic mail
444  *************************************************************************/
445 
446  {
447  struct mailimf_mailbox_list* from = mailimf_mailbox_list_new_empty();
448  mailimf_mailbox_list_add(from, mailimf_mailbox_new(factory->m_from_displayname? mr_encode_header_string(factory->m_from_displayname) : NULL, safe_strdup(factory->m_from_addr)));
449 
450  struct mailimf_address_list* to = NULL;
451  if( factory->m_recipients_names && factory->m_recipients_addr && clist_count(factory->m_recipients_addr)>0 ) {
452  clistiter *iter1, *iter2;
453  to = mailimf_address_list_new_empty();
454  for( iter1=clist_begin(factory->m_recipients_names),iter2=clist_begin(factory->m_recipients_addr); iter1!=NULL&&iter2!=NULL; iter1=clist_next(iter1),iter2=clist_next(iter2)) {
455  const char* name = clist_content(iter1);
456  const char* addr = clist_content(iter2);
457  mailimf_address_list_add(to, mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mailimf_mailbox_new(name? mr_encode_header_string(name) : NULL, safe_strdup(addr)), NULL));
458  }
459  }
460 
461  clist* references_list = NULL;
462  if( factory->m_references ) {
463  references_list = clist_new();
464  clist_append(references_list, (void*)safe_strdup(factory->m_references));
465  }
466 
467  imf_fields = mailimf_fields_new_with_data_all(mailimf_get_date(factory->m_timestamp), from,
468  NULL /* sender */, NULL /* reply-to */,
469  to, NULL /* cc */, NULL /* bcc */, safe_strdup(factory->m_rfc724_mid), NULL /* in-reply-to */,
470  references_list /* references */,
471  NULL /* subject set later */);
472 
473  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-Mailer"),
474  mr_mprintf("Delta Chat %i.%i.%i for %s", MR_VERSION_MAJOR, MR_VERSION_MINOR, MR_VERSION_REVISION, factory->m_mailbox->m_os_name))); /* only informational, for debugging, may be removed in the release. Also do not rely on this as it may be removed by MTAs. */
475 
476  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrMsg"), strdup("1.0"))); /* mark message as being sent by a messenger */
477  if( factory->m_predecessor ) {
478  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrPredecessor"), strdup(factory->m_predecessor)));
479  }
480 
481  if( factory->m_req_mdn ) {
482  /* we use "Chat-Disposition-Notification-To" as replies to "Disposition-Notification-To" are weired in many cases, are just freetext and/or do not follow any standard. */
483  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("Chat-Disposition-Notification-To"), strdup(factory->m_from_addr)));
484  }
485 
486  message = mailmime_new_message_data(NULL);
487  mailmime_set_imf_fields(message, imf_fields);
488  }
489 
490  if( factory->m_loaded == MR_MF_MSG_LOADED )
491  {
492  /* Render a normal message
493  *********************************************************************/
494 
495  mrchat_t* chat = factory->m_chat;
496  mrmsg_t* msg = factory->m_msg;
497 
498  struct mailmime* meta_part = NULL;
499 
500  /* build header etc. */
501  if( chat->m_type==MR_CHAT_TYPE_GROUP )
502  {
503  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrGrpId"), safe_strdup(chat->m_grpid)));
504  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrGrpName"), mr_encode_header_string(chat->m_name)));
505 
506  system_command = mrparam_get_int(msg->m_param, MRP_SYSTEM_CMD, 0);
507  if( system_command == MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP ) {
508  char* email_to_remove = mrparam_get(msg->m_param, MRP_SYSTEM_CMD_PARAM, NULL);
509  if( email_to_remove ) {
510  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrRemoveFromGrp"), email_to_remove));
511  }
512  }
513  else if( system_command == MR_SYSTEM_MEMBER_ADDED_TO_GROUP ) {
514  char* email_to_add = mrparam_get(msg->m_param, MRP_SYSTEM_CMD_PARAM, NULL);
515  if( email_to_add ) {
516  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrAddToGrp"), email_to_add));
517  grpimage = mrparam_get(chat->m_param, MRP_PROFILE_IMAGE, NULL);
518  }
519  }
520  else if( system_command == MR_SYSTEM_GROUPNAME_CHANGED ) {
521  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrGrpNameChanged"), strdup("1")));
522  }
523  else if( system_command == MR_SYSTEM_GROUPIMAGE_CHANGED ) {
524  grpimage = mrparam_get(msg->m_param, MRP_SYSTEM_CMD_PARAM, NULL);
525  if( grpimage==NULL ) {
526  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("Chat-Group-Image"), safe_strdup("0")));
527  }
528  }
529  }
530 
531  if( grpimage )
532  {
533  mrmsg_t* meta = mrmsg_new();
534  meta->m_type = MR_MSG_IMAGE;
535  mrparam_set(meta->m_param, MRP_FILE, grpimage);
536  char* filename_as_sended = NULL;
537  if( (meta_part=build_body_file(meta, "group-image", &filename_as_sended))!=NULL ) {
538  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("Chat-Group-Image"), filename_as_sended/*takes ownership*/));
539  }
540  mrmsg_unref(meta);
541  }
542 
543  if( msg->m_type == MR_MSG_VOICE || msg->m_type == MR_MSG_AUDIO || msg->m_type == MR_MSG_VIDEO )
544  {
545  if( msg->m_type == MR_MSG_VOICE ) {
546  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrVoiceMessage"), strdup("1")));
547  }
548 
549  int duration_ms = mrparam_get_int(msg->m_param, MRP_DURATION, 0);
550  if( duration_ms > 0 ) {
551  mailimf_fields_add(imf_fields, mailimf_field_new_custom(strdup("X-MrDurationMs"), mr_mprintf("%i", (int)duration_ms)));
552  }
553  }
554 
555  /* add text part - we even add empty text and force a MIME-multipart-message as:
556  - some Apps have problems with Non-text in the main part (eg. "Mail" from stock Android)
557  - we can add "forward hints" this way
558  - it looks better */
559  afwd_email = mrparam_exists(msg->m_param, MRP_FORWARDED);
560  char* fwdhint = NULL;
561  if( afwd_email ) {
562  fwdhint = safe_strdup("---------- Forwarded message ----------" LINEEND "From: Delta Chat" LINEEND LINEEND); /* do not chage this! expected this way in the simplifier to detect forwarding! */
563  }
564 
565  int write_m_text = 0;
566  if( msg->m_type==MR_MSG_TEXT && msg->m_text && msg->m_text[0] ) { /* m_text may also contain data otherwise, eg. the filename of attachments */
567  write_m_text = 1;
568  }
569 
570  char* footer = factory->m_selfstatus;
571  message_text = mr_mprintf("%s%s%s%s%s",
572  fwdhint? fwdhint : "",
573  write_m_text? msg->m_text : "",
574  (write_m_text&&footer&&footer[0])? (LINEEND LINEEND) : "",
575  (footer&&footer[0])? ("-- " LINEEND) : "",
576  (footer&&footer[0])? footer : "");
577  struct mailmime* text_part = build_body_text(message_text);
578  mailmime_smart_add_part(message, text_part);
579  parts++;
580 
581  free(fwdhint);
582 
583  /* add attachment part */
584  if( MR_MSG_NEEDS_ATTACHMENT(msg->m_type) ) {
585  struct mailmime* file_part = build_body_file(msg, NULL, NULL);
586  if( file_part ) {
587  mailmime_smart_add_part(message, file_part);
588  parts++;
589  }
590  }
591 
592  if( parts == 0 ) {
593  goto cleanup;
594  }
595 
596  if( meta_part ) {
597  mailmime_smart_add_part(message, meta_part); /* meta parts are only added if there are other parts */
598  parts++;
599  }
600 
601  e2ee_guaranteed = mrparam_get_int(factory->m_msg->m_param, MRP_GUARANTEE_E2EE, 0);
602  }
603  else if( factory->m_loaded == MR_MF_MDN_LOADED )
604  {
605  /* Render a MDN
606  *********************************************************************/
607 
608  struct mailmime* multipart = mailmime_multiple_new("multipart/report"); /* RFC 6522, this also requires the `report-type` parameter which is equal to the MIME subtype of the second body part of the multipart/report */
609  struct mailmime_content* content = multipart->mm_content_type;
610  clist_append(content->ct_parameters, mailmime_param_new_with_data("report-type", "disposition-notification")); /* RFC */
611  mailmime_add_part(message, multipart);
612 
613  /* first body part: always human-readable, always REQUIRED by RFC 6522 */
614  char *p1 = NULL, *p2 = NULL;
615  if( mrparam_get_int(factory->m_msg->m_param, MRP_GUARANTEE_E2EE, 0) ) {
616  p1 = mrstock_str(MR_STR_ENCRYPTEDMSG); /* we SHOULD NOT spread encrypted subjects, date etc. in potentially unencrypted MDNs */
617  }
618  else {
619  p1 = mrmsg_get_summarytext(factory->m_msg, APPROX_SUBJECT_CHARS);
620  }
621  p2 = mrstock_str_repl_string(MR_STR_READRCPT_MAILBODY, p1);
622  message_text = mr_mprintf("%s" LINEEND, p2);
623  free(p2);
624  free(p1);
625 
626  struct mailmime* human_mime_part = build_body_text(message_text);
627  mailmime_add_part(multipart, human_mime_part);
628 
629 
630  /* second body part: machine-readable, always REQUIRED by RFC 6522 */
631  message_text2 = mr_mprintf(
632  "Reporting-UA: Delta Chat %i.%i.%i" LINEEND
633  "Original-Recipient: rfc822;%s" LINEEND
634  "Final-Recipient: rfc822;%s" LINEEND
635  "Original-Message-ID: <%s>" LINEEND
636  "Disposition: manual-action/MDN-sent-automatically; displayed" LINEEND, /* manual-action: the user has configured the MUA to send MDNs (automatic-action implies the receipts cannot be disabled) */
637  MR_VERSION_MAJOR, MR_VERSION_MINOR, MR_VERSION_REVISION,
638  factory->m_from_addr,
639  factory->m_from_addr,
640  factory->m_msg->m_rfc724_mid);
641 
642  struct mailmime_content* content_type = mailmime_content_new_with_str("message/disposition-notification");
643  struct mailmime_fields* mime_fields = mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT);
644  struct mailmime* mach_mime_part = mailmime_new_empty(content_type, mime_fields);
645  mailmime_set_body_text(mach_mime_part, message_text2, strlen(message_text2));
646 
647  mailmime_add_part(multipart, mach_mime_part);
648 
649  /* currently, we do not send MDNs encrypted:
650  - in a multi-device-setup that is not set up properly, MDNs would disturb the communication as they
651  are send automatically which may lead to spreading outdated Autocrypt headers.
652  - they do not carry any information but the Message-ID
653  - this save some KB
654  - in older versions, we did not encrypt messages to ourself when they to to SMTP - however, if these messages
655  are forwarded for any reasons (eg. gmail always forwards to IMAP), we have no chance to decrypt them;
656  this issue is fixed with 0.9.4 */
657  force_unencrypted = 1;
658  }
659  else
660  {
661  goto cleanup;
662  }
663 
664 
665  /* Encrypt the message
666  *************************************************************************/
667 
668  if( !force_unencrypted ) {
669  if( encrypt_to_self==0 || e2ee_guaranteed ) {
670  /* we're here (1) _always_ on SMTP and (2) on IMAP _only_ if SMTP was encrypted before */
671  mrmailbox_e2ee_encrypt(factory->m_mailbox, factory->m_recipients_addr, e2ee_guaranteed, encrypt_to_self, message, &e2ee_helper);
672  }
673  }
674 
675  /* add a subject line */
676  if( e2ee_helper.m_encryption_successfull ) {
677  char* e = mrstock_str(MR_STR_ENCRYPTEDMSG); subject_str = mr_mprintf(MR_CHAT_PREFIX " %s", e); free(e);
678  factory->m_out_encrypted = 1;
679  }
680  else {
681  if( factory->m_loaded==MR_MF_MDN_LOADED ) {
682  char* e = mrstock_str(MR_STR_READRCPT); subject_str = mr_mprintf(MR_CHAT_PREFIX " %s", e); free(e);
683  }
684  else {
685  subject_str = get_subject(factory->m_chat, factory->m_msg, afwd_email);
686  }
687  }
688  struct mailimf_subject* subject = mailimf_subject_new(mr_encode_header_string(subject_str));
689  mailimf_fields_add(imf_fields, mailimf_field_new(MAILIMF_FIELD_SUBJECT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, subject, NULL, NULL, NULL));
690 
691  /* create the full mail and return */
692  factory->m_out = mmap_string_new("");
693  mailmime_write_mem(factory->m_out, &col, message);
694 
695  //{char* t4=mr_null_terminate(ret->str,ret->len); printf("MESSAGE:\n%s\n",t4);free(t4);}
696 
697  success = 1;
698 
699 cleanup:
700  if( message ) {
701  mailmime_free(message);
702  }
703  mrmailbox_e2ee_thanks(&e2ee_helper); /* frees data referenced by "mailmime" but not freed by mailmime_free() */
704  free(message_text); free(message_text2); /* mailmime_set_body_text() does not take ownership of "text" */
705  free(subject_str);
706  free(grpimage);
707  return success;
708 }
709 
int mrparam_exists(mrparam_t *param, int key)
Check if a parameter exists.
Definition: mrparam.c:161
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
-
int m_type
Message type as one of the MR_MSG_* contstants.
Definition: mrmsg.h:60
-
char * mrmsg_get_summarytext(mrmsg_t *msg, int approx_characters)
Get a message summary as a single line of text.
Definition: mrmsg.c:340
-
mrcontact_t * mrcontact_new()
Create a new contact object in memory.
Definition: mrcontact.c:32
-
mrmsg_t * mrmsg_new()
Create new message object.
Definition: mrmsg.c:41
-
char * mrparam_get(mrparam_t *param, int key, const char *def)
Get value of a parameter.
Definition: mrparam.c:186
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
void mrparam_set(mrparam_t *param, int key, const char *value)
Set parameter to a string.
Definition: mrparam.c:253
-
mrparam_t * m_param
!= NULL
Definition: mrchat.h:60
-
char * m_name
NULL if unset.
Definition: mrchat.h:55
-
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
-
mrparam_t * m_param
MRP_FILE, MRP_WIDTH, MRP_HEIGHT etc.
Definition: mrmsg.h:73
-
char * m_text
message text or NULL if unset
Definition: mrmsg.h:72
-
void mrmsg_unref(mrmsg_t *msg)
Free an mrmsg_t object created eg.
Definition: mrmsg.c:66
-
char * m_authname
may be NULL or empty, this is the name authorized by the sender, only this name may be speaded to oth...
Definition: mrcontact.h:46
-
int m_blocked
Blocked state.
Definition: mrcontact.h:48
-
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
-
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
- - - - diff --git a/docs/user/html/mrmimeparser_8c_source.html b/docs/user/html/mrmimeparser_8c_source.html deleted file mode 100644 index ea64db3b..00000000 --- a/docs/user/html/mrmimeparser_8c_source.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmimeparser.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmimeparser.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrmimeparser.h"
25 #include "mrmimefactory.h"
26 #include "mrsimplify.h"
27 
28 
29 /*******************************************************************************
30  * debug output
31  ******************************************************************************/
32 
33 
34 #ifdef MR_USE_MIME_DEBUG
35 
36 /* if you need this functionality, define MR_USE_MIME_DEBUG in the project,
37 eg. in Codeblocks at "Project / Build options / <project or target> / Compiler settings / #defines" */
38 
39 
40 static void display_mime_content(struct mailmime_content * content_type);
41 
42 static void display_mime_data(struct mailmime_data * data)
43 {
44  switch (data->dt_type) {
45  case MAILMIME_DATA_TEXT:
46  printf("data : %i bytes\n", (int) data->dt_data.dt_text.dt_length);
47  break;
48  case MAILMIME_DATA_FILE:
49  printf("data (file) : %s\n", data->dt_data.dt_filename);
50  break;
51  }
52 }
53 
54 static void display_mime_dsp_parm(struct mailmime_disposition_parm * param)
55 {
56  switch (param->pa_type) {
57  case MAILMIME_DISPOSITION_PARM_FILENAME:
58  printf("filename: %s\n", param->pa_data.pa_filename);
59  break;
60  }
61 }
62 
63 static void display_mime_disposition(struct mailmime_disposition * disposition)
64 {
65  clistiter * cur;
66 
67  for(cur = clist_begin(disposition->dsp_parms) ;
68  cur != NULL ; cur = clist_next(cur)) {
69  struct mailmime_disposition_parm * param;
70 
71  param = (struct mailmime_disposition_parm*)clist_content(cur);
72  display_mime_dsp_parm(param);
73  }
74 }
75 
76 static void display_mime_field(struct mailmime_field * field)
77 {
78  switch (field->fld_type) {
79  case MAILMIME_FIELD_TYPE:
80  printf("content-type: ");
81  display_mime_content(field->fld_data.fld_content);
82  printf("\n");
83  break;
84  case MAILMIME_FIELD_DISPOSITION:
85  display_mime_disposition(field->fld_data.fld_disposition);
86  break;
87  }
88 }
89 
90 static void display_mime_fields(struct mailmime_fields * fields)
91 {
92  clistiter * cur;
93 
94  for(cur = clist_begin(fields->fld_list) ; cur != NULL ; cur = clist_next(cur)) {
95  struct mailmime_field * field;
96 
97  field = (struct mailmime_field*)clist_content(cur);
98  display_mime_field(field);
99  }
100 }
101 
102 static void display_date_time(struct mailimf_date_time * d)
103 {
104  printf("%02i/%02i/%i %02i:%02i:%02i %+04i",
105  d->dt_day, d->dt_month, d->dt_year,
106  d->dt_hour, d->dt_min, d->dt_sec, d->dt_zone);
107 }
108 
109 static void display_orig_date(struct mailimf_orig_date * orig_date)
110 {
111  display_date_time(orig_date->dt_date_time);
112 }
113 
114 static void display_mailbox(struct mailimf_mailbox * mb)
115 {
116  if (mb->mb_display_name != NULL)
117  printf("%s ", mb->mb_display_name);
118  printf("<%s>", mb->mb_addr_spec);
119 }
120 
121 static void display_mailbox_list(struct mailimf_mailbox_list * mb_list)
122 {
123  clistiter * cur;
124 
125  for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
126  cur = clist_next(cur)) {
127  struct mailimf_mailbox * mb;
128 
129  mb = (struct mailimf_mailbox*)clist_content(cur);
130 
131  display_mailbox(mb);
132  if (clist_next(cur) != NULL) {
133  printf(", ");
134  }
135  }
136 }
137 
138 static void display_group(struct mailimf_group * group)
139 {
140  clistiter * cur;
141 
142  printf("%s: ", group->grp_display_name);
143  for(cur = clist_begin(group->grp_mb_list->mb_list) ; cur != NULL ; cur = clist_next(cur)) {
144  struct mailimf_mailbox * mb;
145 
146  mb = (struct mailimf_mailbox*)clist_content(cur);
147  display_mailbox(mb);
148  }
149  printf("; ");
150 }
151 
152 static void display_address(struct mailimf_address * a)
153 {
154  switch (a->ad_type) {
155  case MAILIMF_ADDRESS_GROUP:
156  display_group(a->ad_data.ad_group);
157  break;
158 
159  case MAILIMF_ADDRESS_MAILBOX:
160  display_mailbox(a->ad_data.ad_mailbox);
161  break;
162  }
163 }
164 
165 static void display_address_list(struct mailimf_address_list * addr_list)
166 {
167  clistiter * cur;
168 
169  for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
170  cur = clist_next(cur)) {
171  struct mailimf_address * addr;
172 
173  addr = (struct mailimf_address*)clist_content(cur);
174 
175  display_address(addr);
176 
177  if (clist_next(cur) != NULL) {
178  printf(", ");
179  }
180  }
181 }
182 
183 static void display_from(struct mailimf_from * from)
184 {
185  display_mailbox_list(from->frm_mb_list);
186 }
187 
188 static void display_to(struct mailimf_to * to)
189 {
190  display_address_list(to->to_addr_list);
191 }
192 
193 static void display_cc(struct mailimf_cc * cc)
194 {
195  display_address_list(cc->cc_addr_list);
196 }
197 
198 static void display_subject(struct mailimf_subject * subject)
199 {
200  printf("%s", subject->sbj_value);
201 }
202 
203 static void display_field(struct mailimf_field * field)
204 {
205  switch (field->fld_type) {
206  case MAILIMF_FIELD_ORIG_DATE:
207  printf("Date: ");
208  display_orig_date(field->fld_data.fld_orig_date);
209  printf("\n");
210  break;
211  case MAILIMF_FIELD_FROM:
212  printf("From: ");
213  display_from(field->fld_data.fld_from);
214  printf("\n");
215  break;
216  case MAILIMF_FIELD_TO:
217  printf("To: ");
218  display_to(field->fld_data.fld_to);
219  printf("\n");
220  break;
221  case MAILIMF_FIELD_CC:
222  printf("Cc: ");
223  display_cc(field->fld_data.fld_cc);
224  printf("\n");
225  break;
226  case MAILIMF_FIELD_SUBJECT:
227  printf("Subject: ");
228  display_subject(field->fld_data.fld_subject);
229  printf("\n");
230  break;
231  case MAILIMF_FIELD_MESSAGE_ID:
232  printf("Message-ID: %s\n", field->fld_data.fld_message_id->mid_value);
233  break;
234  }
235 }
236 
237 static void display_fields(struct mailimf_fields * fields)
238 {
239  clistiter * cur;
240 
241  for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
242  cur = clist_next(cur)) {
243  struct mailimf_field * f;
244 
245  f = (struct mailimf_field*)clist_content(cur);
246 
247  display_field(f);
248  }
249 }
250 
251 static void display_mime_discrete_type(struct mailmime_discrete_type * discrete_type)
252 {
253  switch (discrete_type->dt_type) {
254  case MAILMIME_DISCRETE_TYPE_TEXT:
255  printf("text");
256  break;
257  case MAILMIME_DISCRETE_TYPE_IMAGE:
258  printf("image");
259  break;
260  case MAILMIME_DISCRETE_TYPE_AUDIO:
261  printf("audio");
262  break;
263  case MAILMIME_DISCRETE_TYPE_VIDEO:
264  printf("video");
265  break;
266  case MAILMIME_DISCRETE_TYPE_APPLICATION:
267  printf("application");
268  break;
269  case MAILMIME_DISCRETE_TYPE_EXTENSION:
270  printf("%s", discrete_type->dt_extension);
271  break;
272  }
273 }
274 
275 static void display_mime_composite_type(struct mailmime_composite_type * ct)
276 {
277  switch (ct->ct_type) {
278  case MAILMIME_COMPOSITE_TYPE_MESSAGE:
279  printf("message");
280  break;
281  case MAILMIME_COMPOSITE_TYPE_MULTIPART:
282  printf("multipart");
283  break;
284  case MAILMIME_COMPOSITE_TYPE_EXTENSION:
285  printf("%s", ct->ct_token);
286  break;
287  }
288 }
289 
290 static void display_mime_type(struct mailmime_type * type)
291 {
292  switch (type->tp_type) {
293  case MAILMIME_TYPE_DISCRETE_TYPE:
294  display_mime_discrete_type(type->tp_data.tp_discrete_type);
295  break;
296  case MAILMIME_TYPE_COMPOSITE_TYPE:
297  display_mime_composite_type(type->tp_data.tp_composite_type);
298  break;
299  }
300 }
301 
302 static void display_mime_content(struct mailmime_content * content_type)
303 {
304  printf("type: ");
305  display_mime_type(content_type->ct_type);
306  printf("/%s\n", content_type->ct_subtype);
307 }
308 
309 static void print_mime(struct mailmime * mime)
310 {
311  clistiter * cur;
312 
313  if( mime == NULL ) {
314  printf("ERROR: NULL given to print_mime()\n");
315  return;
316  }
317 
318  switch (mime->mm_type) {
319  case MAILMIME_SINGLE:
320  printf("single part\n");
321  break;
322  case MAILMIME_MULTIPLE:
323  printf("multipart\n");
324  break;
325  case MAILMIME_MESSAGE:
326  printf("message\n");
327  break;
328  }
329 
330  if (mime->mm_mime_fields != NULL) {
331  if (clist_begin(mime->mm_mime_fields->fld_list) != NULL) {
332  printf("--------------------------------<mime-headers>--------------------------------\n");
333  display_mime_fields(mime->mm_mime_fields);
334  printf("--------------------------------</mime-headers>-------------------------------\n");
335  }
336  }
337 
338  display_mime_content(mime->mm_content_type);
339 
340  switch (mime->mm_type) {
341  case MAILMIME_SINGLE:
342  display_mime_data(mime->mm_data.mm_single);
343  break;
344 
345  case MAILMIME_MULTIPLE:
346  for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ; cur != NULL ; cur = clist_next(cur)) {
347  printf("---------------------------<mime-part-of-multiple>----------------------------\n");
348  print_mime((struct mailmime*)clist_content(cur));
349  printf("---------------------------</mime-part-of-multiple>---------------------------\n");
350  }
351  break;
352 
353  case MAILMIME_MESSAGE:
354  if (mime->mm_data.mm_message.mm_fields) {
355  if (clist_begin(mime->mm_data.mm_message.mm_fields->fld_list) != NULL) {
356  printf("-------------------------------<email-headers>--------------------------------\n");
357  display_fields(mime->mm_data.mm_message.mm_fields);
358  printf("-------------------------------</email-headers>-------------------------------\n");
359  }
360 
361  if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
362  printf("----------------------------<mime-part-of-message>----------------------------\n");
363  print_mime(mime->mm_data.mm_message.mm_msg_mime);
364  printf("----------------------------</mime-part-of-message>---------------------------\n");
365  }
366  }
367  break;
368  }
369 }
370 
371 
372 void mr_print_mime(struct mailmime* mime)
373 {
374  printf("====================================<mime>====================================\n");
375  print_mime(mime);
376  printf("====================================</mime>===================================\n\n");
377 }
378 
379 
380 #endif /* DEBUG_MIME_OUTPUT */
381 
382 
383 /*******************************************************************************
384  * low-level-tools for working with mailmime structures directly
385  ******************************************************************************/
386 
387 
388 struct mailimf_fields* mr_find_mailimf_fields(struct mailmime* mime)
389 {
390  if( mime == NULL ) {
391  return NULL;
392  }
393 
394  clistiter* cur;
395  switch (mime->mm_type) {
396  case MAILMIME_MULTIPLE:
397  for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ; cur != NULL ; cur = clist_next(cur)) {
398  struct mailimf_fields* header = mr_find_mailimf_fields(clist_content(cur));
399  if( header ) {
400  return header;
401  }
402  }
403  break;
404 
405  case MAILMIME_MESSAGE:
406  return mime->mm_data.mm_message.mm_fields;
407  }
408 
409  return NULL;
410 }
411 
412 
413 struct mailimf_field* mr_find_mailimf_field(struct mailimf_fields* header, int wanted_fld_type)
414 {
415  if( header == NULL || header->fld_list == NULL ) {
416  return NULL;
417  }
418 
419  clistiter* cur1;
420  for( cur1 = clist_begin(header->fld_list); cur1!=NULL ; cur1=clist_next(cur1) )
421  {
422  struct mailimf_field* field = (struct mailimf_field*)clist_content(cur1);
423  if( field )
424  {
425  if( field->fld_type == wanted_fld_type ) {
426  return field;
427  }
428  }
429  }
430 
431  return NULL;
432 }
433 
434 
435 struct mailimf_optional_field* mr_find_mailimf_field2(struct mailimf_fields* header, const char* wanted_fld_name)
436 {
437  /* Note: the function does not return fields with no value set! */
438  if( header == NULL || header->fld_list == NULL ) {
439  return NULL;
440  }
441 
442  clistiter* cur1;
443  for( cur1 = clist_begin(header->fld_list); cur1!=NULL ; cur1=clist_next(cur1) )
444  {
445  struct mailimf_field* field = (struct mailimf_field*)clist_content(cur1);
446  if( field && field->fld_type == MAILIMF_FIELD_OPTIONAL_FIELD )
447  {
448  struct mailimf_optional_field* optional_field = field->fld_data.fld_optional_field;
449  if( optional_field && optional_field->fld_name && optional_field->fld_value && strcasecmp(optional_field->fld_name, wanted_fld_name)==0 ) {
450  return optional_field;
451  }
452  }
453  }
454 
455  return NULL;
456 }
457 
458 
459 struct mailmime_parameter* mr_find_ct_parameter(struct mailmime* mime, const char* name)
460 {
461  /* find a parameter in `Content-Type: foo/bar; name=value;` */
462  if( mime==NULL || name==NULL
463  || mime->mm_content_type==NULL || mime->mm_content_type->ct_parameters==NULL )
464  {
465  return NULL;
466  }
467 
468  clistiter* cur;
469  for( cur = clist_begin(mime->mm_content_type->ct_parameters); cur != NULL; cur = clist_next(cur) ) {
470  struct mailmime_parameter* param = (struct mailmime_parameter*)clist_content(cur);
471  if( param && param->pa_name ) {
472  if( strcmp(param->pa_name, name)==0 ) {
473  return param;
474  }
475  }
476  }
477 
478  return NULL;
479 }
480 
481 
482 char* mr_normalize_addr(const char* addr__)
483 {
484  /* Not sure if we should also unifiy international characters before the @,
485  see also https://autocrypt.readthedocs.io/en/latest/address-canonicalization.html */
486  char* addr = safe_strdup(addr__);
487  mr_trim(addr);
488  if( strncmp(addr, "mailto:", 7)==0 ) {
489  char* old = addr;
490  addr = safe_strdup(&old[7]);
491  free(old);
492  mr_trim(addr);
493  }
494  return addr;
495 }
496 
497 
498 char* mr_find_first_addr(const struct mailimf_mailbox_list* mb_list)
499 {
500  clistiter* cur;
501 
502  if( mb_list == NULL ) {
503  return NULL;
504  }
505 
506  for( cur = clist_begin(mb_list->mb_list); cur!=NULL ; cur=clist_next(cur) ) {
507  struct mailimf_mailbox* mb = (struct mailimf_mailbox*)clist_content(cur);
508  if( mb && mb->mb_addr_spec ) {
509  return mr_normalize_addr(mb->mb_addr_spec);
510  }
511  }
512  return NULL;
513 }
514 
515 
516 /*******************************************************************************
517  * a MIME part
518  ******************************************************************************/
519 
520 
521 static mrmimepart_t* mrmimepart_new(void)
522 {
523  mrmimepart_t* ths = NULL;
524 
525  if( (ths=calloc(1, sizeof(mrmimepart_t)))==NULL ) {
526  exit(33);
527  }
528 
529  ths->m_type = MR_MSG_UNDEFINED;
530  ths->m_param = mrparam_new();
531 
532  return ths;
533 }
534 
535 
536 static void mrmimepart_unref(mrmimepart_t* ths)
537 {
538  if( ths == NULL ) {
539  return;
540  }
541 
542  if( ths->m_msg ) {
543  free(ths->m_msg);
544  ths->m_msg = NULL;
545  }
546 
547  if( ths->m_msg_raw ) {
548  free(ths->m_msg_raw);
549  ths->m_msg_raw = NULL;
550  }
551 
552  mrparam_unref(ths->m_param);
553  free(ths);
554 }
555 
556 
557 /*******************************************************************************
558  * Main interface
559  ******************************************************************************/
560 
561 
562 mrmimeparser_t* mrmimeparser_new(const char* blobdir, mrmailbox_t* mailbox)
563 {
564  mrmimeparser_t* ths = NULL;
565 
566  if( (ths=calloc(1, sizeof(mrmimeparser_t)))==NULL ) {
567  exit(30);
568  }
569 
570  ths->m_mailbox = mailbox;
571  ths->m_parts = carray_new(16);
572  ths->m_blobdir = blobdir; /* no need to copy the string at the moment */
573  ths->m_reports = carray_new(16);
574 
575  return ths;
576 }
577 
578 
579 void mrmimeparser_unref(mrmimeparser_t* ths)
580 {
581  if( ths == NULL ) {
582  return;
583  }
584 
585  mrmimeparser_empty(ths);
586  if( ths->m_parts ) { carray_free(ths->m_parts); }
587  if( ths->m_reports ) { carray_free(ths->m_reports); }
588  free(ths);
589 }
590 
591 
592 void mrmimeparser_empty(mrmimeparser_t* ths)
593 {
594  if( ths == NULL ) {
595  return;
596  }
597 
598  if( ths->m_parts )
599  {
600  int i, cnt = carray_count(ths->m_parts);
601  for( i = 0; i < cnt; i++ ) {
602  mrmimepart_t* part = (mrmimepart_t*)carray_get(ths->m_parts, i);
603  if( part ) {
604  mrmimepart_unref(part);
605  }
606  }
607  carray_set_size(ths->m_parts, 0);
608  }
609 
610  ths->m_header = NULL; /* a pointer somewhere to the MIME data, must not be freed */
611  ths->m_is_send_by_messenger = 0;
612  ths->m_is_system_message = 0;
613 
614  free(ths->m_subject);
615  ths->m_subject = NULL;
616 
617  if( ths->m_mimeroot )
618  {
619  mailmime_free(ths->m_mimeroot);
620  ths->m_mimeroot = NULL;
621  }
622 
623  ths->m_is_forwarded = 0;
624 
625  if( ths->m_reports ) {
626  carray_set_size(ths->m_reports, 0);
627  }
628 
629  ths->m_decrypted_and_validated = 0;
630  ths->m_decrypted_with_validation_errors = 0;
631  ths->m_decrypting_failed = 0;
632 }
633 
634 
635 static int is_attachment_disposition(struct mailmime* mime)
636 {
637  if( mime->mm_mime_fields != NULL ) {
638  clistiter* cur;
639  for( cur = clist_begin(mime->mm_mime_fields->fld_list); cur != NULL; cur = clist_next(cur) ) {
640  struct mailmime_field* field = (struct mailmime_field*)clist_content(cur);
641  if( field && field->fld_type == MAILMIME_FIELD_DISPOSITION && field->fld_data.fld_disposition ) {
642  if( field->fld_data.fld_disposition->dsp_type
643  && field->fld_data.fld_disposition->dsp_type->dsp_type==MAILMIME_DISPOSITION_TYPE_ATTACHMENT )
644  {
645  return 1;
646  }
647  }
648  }
649  }
650  return 0;
651 }
652 
653 
654 static int mrmimeparser_get_mime_type(struct mailmime* mime, int* msg_type)
655 {
656  #define MR_MIMETYPE_MP_ALTERNATIVE 10
657  #define MR_MIMETYPE_MP_RELATED 20
658  #define MR_MIMETYPE_MP_MIXED 30
659  #define MR_MIMETYPE_MP_NOT_DECRYPTABLE 40
660  #define MR_MIMETYPE_MP_REPORT 45
661  #define MR_MIMETYPE_MP_SIGNED 46
662  #define MR_MIMETYPE_MP_OTHER 50
663  #define MR_MIMETYPE_TEXT_PLAIN 60
664  #define MR_MIMETYPE_TEXT_HTML 70
665  #define MR_MIMETYPE_IMAGE 80
666  #define MR_MIMETYPE_AUDIO 90
667  #define MR_MIMETYPE_VIDEO 100
668  #define MR_MIMETYPE_FILE 110
669 
670  struct mailmime_content* c = mime->mm_content_type;
671  int dummy; if( msg_type == NULL ) { msg_type = &dummy; }
672  *msg_type = MR_MSG_UNDEFINED;
673 
674  if( c == NULL || c->ct_type == NULL ) {
675  return 0;
676  }
677 
678  switch( c->ct_type->tp_type )
679  {
680  case MAILMIME_TYPE_DISCRETE_TYPE:
681  switch( c->ct_type->tp_data.tp_discrete_type->dt_type )
682  {
683  case MAILMIME_DISCRETE_TYPE_TEXT:
684  if( is_attachment_disposition(mime) ) {
685  ; /* MR_MIMETYPE_FILE is returned below - we leave text attachments as attachments as they may be too large to display as a normal message, eg. complete books. */
686  }
687  else if( strcmp(c->ct_subtype, "plain")==0 ) {
688  *msg_type = MR_MSG_TEXT;
689  return MR_MIMETYPE_TEXT_PLAIN;
690  }
691  else if( strcmp(c->ct_subtype, "html")==0 ) {
692  *msg_type = MR_MSG_TEXT;
693  return MR_MIMETYPE_TEXT_HTML;
694  }
695  *msg_type = MR_MSG_FILE;
696  return MR_MIMETYPE_FILE;
697 
698  case MAILMIME_DISCRETE_TYPE_IMAGE:
699  if( strcmp(c->ct_subtype, "gif")==0 ) {
700  *msg_type = MR_MSG_GIF;
701  }
702  else {
703  *msg_type = MR_MSG_IMAGE;
704  }
705  return MR_MIMETYPE_IMAGE;
706 
707  case MAILMIME_DISCRETE_TYPE_AUDIO:
708  *msg_type = MR_MSG_AUDIO; /* we correct this later to MR_MSG_VOICE, currently, this is not possible as we do not know the main header */
709  return MR_MIMETYPE_AUDIO;
710 
711  case MAILMIME_DISCRETE_TYPE_VIDEO:
712  *msg_type = MR_MSG_VIDEO;
713  return MR_MIMETYPE_VIDEO;
714 
715  default:
716  *msg_type = MR_MSG_FILE;
717  return MR_MIMETYPE_FILE;
718  }
719  break;
720 
721  case MAILMIME_TYPE_COMPOSITE_TYPE:
722  if( c->ct_type->tp_data.tp_composite_type->ct_type == MAILMIME_COMPOSITE_TYPE_MULTIPART )
723  {
724  if( strcmp(c->ct_subtype, "alternative")==0 ) {
725  return MR_MIMETYPE_MP_ALTERNATIVE;
726  }
727  else if( strcmp(c->ct_subtype, "related")==0 ) {
728  return MR_MIMETYPE_MP_RELATED;
729  }
730  else if( strcmp(c->ct_subtype, "encrypted")==0 ) {
731  return MR_MIMETYPE_MP_NOT_DECRYPTABLE; /* decryptable parts are already converted to other mime parts in mre2ee_decrypt() */
732  }
733  else if( strcmp(c->ct_subtype, "signed")==0 ) {
734  return MR_MIMETYPE_MP_SIGNED;
735  }
736  else if( strcmp(c->ct_subtype, "mixed")==0 ) {
737  return MR_MIMETYPE_MP_MIXED;
738  }
739  else if( strcmp(c->ct_subtype, "report")==0 ) {
740  return MR_MIMETYPE_MP_REPORT;
741  }
742  else {
743  return MR_MIMETYPE_MP_OTHER;
744  }
745  }
746  else if( c->ct_type->tp_data.tp_composite_type->ct_type == MAILMIME_COMPOSITE_TYPE_MESSAGE )
747  {
748  /* Enacapsulated messages, see https://www.w3.org/Protocols/rfc1341/7_3_Message.html
749  Also used as part "message/disposition-notification" of "multipart/report", which, however, will be handled separatedly.
750  I've not seen any messages using this, so we do not attach these parts (maybe they're used to attach replies, which are unwanted at all).
751 
752  For now, we skip these parts at all; if desired, we could return MR_MIMETYPE_FILE/MR_MSG_FILE for selected and known subparts. */
753  return 0;
754  }
755  break;
756 
757  default:
758  break;
759  }
760 
761  return 0; /* unknown */
762 }
763 
764 
765 #if 0 /* currently no needed */
766 static char* get_file_disposition_suffix_(struct mailmime_disposition* file_disposition)
767 {
768  if( file_disposition ) {
769  clistiter* cur;
770  for( cur = clist_begin(file_disposition->dsp_parms); cur != NULL; cur = clist_next(cur) ) {
771  struct mailmime_disposition_parm* dsp_param = (struct mailmime_disposition_parm*)clist_content(cur);
772  if( dsp_param ) {
773  if( dsp_param->pa_type==MAILMIME_DISPOSITION_PARM_FILENAME ) {
774  return mr_get_filesuffix_lc(dsp_param->pa_data.pa_filename);
775  }
776  }
777  }
778  }
779  return NULL;
780 }
781 #endif
782 
783 
784 int mr_mime_transfer_decode(struct mailmime* mime, const char** ret_decoded_data, size_t* ret_decoded_data_bytes, char** ret_to_mmap_string_unref)
785 {
786  int mime_transfer_encoding = MAILMIME_MECHANISM_BINARY;
787  struct mailmime_data* mime_data = mime->mm_data.mm_single;
788  const char* decoded_data = NULL; /* must not be free()'d */
789  size_t decoded_data_bytes = 0;
790  char* transfer_decoding_buffer = NULL; /* mmap_string_unref()'d if set */
791 
792  if( mime == NULL || ret_decoded_data == NULL || ret_decoded_data_bytes == NULL || ret_to_mmap_string_unref == NULL
793  || *ret_decoded_data != NULL || *ret_decoded_data_bytes != 0 || *ret_to_mmap_string_unref != NULL ) {
794  return 0;
795  }
796 
797  if( mime->mm_mime_fields != NULL ) {
798  clistiter* cur;
799  for( cur = clist_begin(mime->mm_mime_fields->fld_list); cur != NULL; cur = clist_next(cur) ) {
800  struct mailmime_field* field = (struct mailmime_field*)clist_content(cur);
801  if( field && field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING && field->fld_data.fld_encoding ) {
802  mime_transfer_encoding = field->fld_data.fld_encoding->enc_type;
803  break;
804  }
805  }
806  }
807 
808  /* regard `Content-Transfer-Encoding:` */
809  if( mime_transfer_encoding == MAILMIME_MECHANISM_7BIT
810  || mime_transfer_encoding == MAILMIME_MECHANISM_8BIT
811  || mime_transfer_encoding == MAILMIME_MECHANISM_BINARY )
812  {
813  decoded_data = mime_data->dt_data.dt_text.dt_data;
814  decoded_data_bytes = mime_data->dt_data.dt_text.dt_length;
815  if( decoded_data == NULL || decoded_data_bytes <= 0 ) {
816  return 0; /* no error - but no data */
817  }
818  }
819  else
820  {
821  int r;
822  size_t current_index = 0;
823  r = mailmime_part_parse(mime_data->dt_data.dt_text.dt_data, mime_data->dt_data.dt_text.dt_length,
824  &current_index, mime_transfer_encoding,
825  &transfer_decoding_buffer, &decoded_data_bytes);
826  if( r != MAILIMF_NO_ERROR || transfer_decoding_buffer == NULL || decoded_data_bytes <= 0 ) {
827  return 0;
828  }
829  decoded_data = transfer_decoding_buffer;
830  }
831 
832  *ret_decoded_data = decoded_data;
833  *ret_decoded_data_bytes = decoded_data_bytes;
834  *ret_to_mmap_string_unref = transfer_decoding_buffer;
835  return 1;
836 }
837 
838 
839 static int mrmimeparser_add_single_part_if_known(mrmimeparser_t* ths, struct mailmime* mime)
840 {
841  mrmimepart_t* part = mrmimepart_new();
842  int do_add_part = 0;
843 
844  int mime_type;
845  struct mailmime_data* mime_data;
846  char* pathNfilename = NULL;
847  char* file_suffix = NULL, *desired_filename = NULL;
848  int msg_type;
849 
850  char* transfer_decoding_buffer = NULL; /* mmap_string_unref()'d if set */
851  char* charset_buffer = NULL; /* charconv_buffer_free()'d if set (just calls mmap_string_unref()) */
852  const char* decoded_data = NULL; /* must not be free()'d */
853  size_t decoded_data_bytes = 0;
854  mrsimplify_t* simplifier = NULL;
855 
856  if( mime == NULL || mime->mm_data.mm_single == NULL || part == NULL ) {
857  goto cleanup;
858  }
859 
860  /* get mime type from `mime` */
861  mime_type = mrmimeparser_get_mime_type(mime, &msg_type);
862 
863  /* get data pointer from `mime` */
864  mime_data = mime->mm_data.mm_single;
865  if( mime_data->dt_type != MAILMIME_DATA_TEXT /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
866  || mime_data->dt_data.dt_text.dt_data == NULL
867  || mime_data->dt_data.dt_text.dt_length <= 0 ) {
868  goto cleanup;
869  }
870 
871 
872  /* regard `Content-Transfer-Encoding:` */
873  if( !mr_mime_transfer_decode(mime, &decoded_data, &decoded_data_bytes, &transfer_decoding_buffer) ) {
874  goto cleanup; /* no always error - but no data */
875  }
876 
877  switch( mime_type )
878  {
879  case MR_MIMETYPE_TEXT_PLAIN:
880  case MR_MIMETYPE_TEXT_HTML:
881  {
882  if( simplifier==NULL ) {
883  simplifier = mrsimplify_new();
884  if( simplifier==NULL ) {
885  goto cleanup;
886  }
887  }
888 
889  const char* charset = mailmime_content_charset_get(mime->mm_content_type); /* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
890  if( charset!=NULL && strcmp(charset, "utf-8")!=0 && strcmp(charset, "UTF-8")!=0 ) {
891  size_t ret_bytes = 0;
892  int r = charconv_buffer("utf-8", charset, decoded_data, decoded_data_bytes, &charset_buffer, &ret_bytes);
893  if( r != MAIL_CHARCONV_NO_ERROR ) {
894  mrmailbox_log_warning(ths->m_mailbox, 0, "Cannot convert %i bytes from \"%s\" to \"utf-8\"; errorcode is %i.", /* if this warning comes up for usual character sets, maybe libetpan is compiled without iconv? */
895  (int)decoded_data_bytes, charset, (int)r); /* continue, however */
896  }
897  else if( charset_buffer==NULL || ret_bytes <= 0 ) {
898  goto cleanup; /* no error - but nothing to add */
899  }
900  else {
901  decoded_data = charset_buffer;
902  decoded_data_bytes = ret_bytes;
903  }
904  }
905 
906  part->m_type = MR_MSG_TEXT;
907  part->m_msg_raw = strndup(decoded_data, decoded_data_bytes);
908  part->m_msg = mrsimplify_simplify(simplifier, decoded_data, decoded_data_bytes, mime_type==MR_MIMETYPE_TEXT_HTML? 1 : 0);
909 
910  if( part->m_msg && part->m_msg[0] ) {
911  do_add_part = 1;
912  }
913 
914  if( simplifier->m_is_forwarded ) {
915  ths->m_is_forwarded = 1;
916  }
917  }
918  break;
919 
920  case MR_MIMETYPE_IMAGE:
921  case MR_MIMETYPE_AUDIO:
922  case MR_MIMETYPE_VIDEO:
923  case MR_MIMETYPE_FILE:
924  {
925  /* get desired file name */
926  struct mailmime_disposition* file_disposition = NULL; /* must not be free()'d */
927  clistiter* cur;
928  for( cur = clist_begin(mime->mm_mime_fields->fld_list); cur != NULL; cur = clist_next(cur) ) {
929  struct mailmime_field* field = (struct mailmime_field*)clist_content(cur);
930  if( field && field->fld_type == MAILMIME_FIELD_DISPOSITION && field->fld_data.fld_disposition ) {
931  file_disposition = field->fld_data.fld_disposition;
932  break;
933  }
934  }
935 
936  if( file_disposition ) {
937  for( cur = clist_begin(file_disposition->dsp_parms); cur != NULL; cur = clist_next(cur) ) {
938  struct mailmime_disposition_parm* dsp_param = (struct mailmime_disposition_parm*)clist_content(cur);
939  if( dsp_param ) {
940  if( dsp_param->pa_type==MAILMIME_DISPOSITION_PARM_FILENAME ) {
941  desired_filename = safe_strdup(dsp_param->pa_data.pa_filename);
942  }
943  }
944  }
945  }
946 
947  if( desired_filename==NULL ) {
948  struct mailmime_parameter* param = mr_find_ct_parameter(mime, "name");
949  if( param && param->pa_value && param->pa_value[0] ) {
950  desired_filename = safe_strdup(param->pa_value);
951  }
952  }
953 
954  if( desired_filename==NULL ) {
955  if( mime->mm_content_type && mime->mm_content_type->ct_subtype ) {
956  desired_filename = mr_mprintf("file.%s", mime->mm_content_type->ct_subtype);
957  }
958  else {
959  goto cleanup;
960  }
961  }
962 
963  mr_replace_bad_utf8_chars(desired_filename);
964 
965  /* create a free file name to use */
966  if( (pathNfilename=mr_get_fine_pathNfilename(ths->m_blobdir, desired_filename)) == NULL ) {
967  goto cleanup;
968  }
969 
970  /* copy data to file */
971  if( mr_write_file(pathNfilename, decoded_data, decoded_data_bytes, ths->m_mailbox)==0 ) {
972  goto cleanup;
973  }
974 
975  part->m_type = msg_type;
976  part->m_bytes = decoded_data_bytes;
977  mrparam_set(part->m_param, MRP_FILE, pathNfilename);
978  if( MR_MSG_MAKE_FILENAME_SEARCHABLE(msg_type) ) {
979  part->m_msg = mr_get_filename(pathNfilename);
980  }
981  else if( MR_MSG_MAKE_SUFFIX_SEARCHABLE(msg_type) ) {
982  part->m_msg = mr_get_filesuffix_lc(pathNfilename);
983  }
984 
985  if( mime_type == MR_MIMETYPE_IMAGE ) {
986  uint32_t w = 0, h = 0;
987  if( mr_get_filemeta(decoded_data, decoded_data_bytes, &w, &h) ) {
988  mrparam_set_int(part->m_param, MRP_WIDTH, w);
989  mrparam_set_int(part->m_param, MRP_HEIGHT, h);
990  }
991  }
992 
993  /* split author/title from the original filename (if we do it from the real filename, we'll also get numbers appended by mr_get_fine_pathNfilename()) */
994  if( msg_type == MR_MSG_AUDIO ) {
995  char* author = NULL, *title = NULL;
996  mrmsg_get_authorNtitle_from_filename(desired_filename, &author, &title);
997  mrparam_set(part->m_param, MRP_AUTHORNAME, author);
998  mrparam_set(part->m_param, MRP_TRACKNAME, title);
999  free(author);
1000  free(title);
1001  }
1002 
1003  do_add_part = 1;
1004  }
1005  break;
1006 
1007  default:
1008  break;
1009  }
1010 
1011  /* add object? (we do not add all objetcs, eg. signatures etc. are ignored) */
1012 cleanup:
1013  if( simplifier ) {
1014  mrsimplify_unref(simplifier);
1015  }
1016 
1017  if( charset_buffer ) {
1018  charconv_buffer_free(charset_buffer);
1019  }
1020 
1021  if( transfer_decoding_buffer ) {
1022  mmap_string_unref(transfer_decoding_buffer);
1023  }
1024 
1025  free(pathNfilename);
1026  free(file_suffix);
1027  free(desired_filename);
1028 
1029  if( do_add_part ) {
1030  if( ths->m_decrypted_and_validated ) {
1031  mrparam_set_int(part->m_param, MRP_GUARANTEE_E2EE, 1);
1032  }
1033  else if( ths->m_decrypted_with_validation_errors ) {
1034  mrparam_set_int(part->m_param, MRP_ERRONEOUS_E2EE, ths->m_decrypted_with_validation_errors);
1035  }
1036  carray_add(ths->m_parts, (void*)part, NULL);
1037  return 1; /* part used */
1038  }
1039  else {
1040  mrmimepart_unref(part);
1041  return 0;
1042  }
1043 }
1044 
1045 
1046 static int mrmimeparser_parse_mime_recursive(mrmimeparser_t* ths, struct mailmime* mime)
1047 {
1048  int any_part_added = 0;
1049  clistiter* cur;
1050 
1051  if( ths == NULL || mime == NULL ) {
1052  return 0;
1053  }
1054 
1055  switch( mime->mm_type )
1056  {
1057  case MAILMIME_SINGLE:
1058  any_part_added = mrmimeparser_add_single_part_if_known(ths, mime);
1059  break;
1060 
1061  case MAILMIME_MULTIPLE:
1062  switch( mrmimeparser_get_mime_type(mime, NULL) )
1063  {
1064  case MR_MIMETYPE_MP_ALTERNATIVE: /* add "best" part */
1065  /* Most times, mutlipart/alternative contains true alternatives as text/plain and text/html.
1066  If we find a multipart/mixed inside mutlipart/alternative, we use this (happens eg in apple mail: "plaintext" as an alternative to "html+PDF attachment") */
1067  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
1068  struct mailmime* childmime = (struct mailmime*)clist_content(cur);
1069  if( mrmimeparser_get_mime_type(childmime, NULL) == MR_MIMETYPE_MP_MIXED ) {
1070  any_part_added = mrmimeparser_parse_mime_recursive(ths, childmime);
1071  break;
1072  }
1073  }
1074 
1075 
1076  if( !any_part_added ) {
1077  /* search for text/plain and add this */
1078  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
1079  struct mailmime* childmime = (struct mailmime*)clist_content(cur);
1080  if( mrmimeparser_get_mime_type(childmime, NULL) == MR_MIMETYPE_TEXT_PLAIN ) {
1081  any_part_added = mrmimeparser_parse_mime_recursive(ths, childmime);
1082  break;
1083  }
1084  }
1085  }
1086 
1087  if( !any_part_added ) { /* `text/plain` not found - use the first part */
1088  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
1089  if( mrmimeparser_parse_mime_recursive(ths, (struct mailmime*)clist_content(cur)) ) {
1090  any_part_added = 1;
1091  break; /* out of for() */
1092  }
1093  }
1094  }
1095  break;
1096 
1097  case MR_MIMETYPE_MP_RELATED: /* add the "root part" - the other parts may be referenced which is not interesting for us (eg. embedded images) */
1098  /* we assume he "root part" being the first one, which may not be always true ... however, most times it seems okay. */
1099  cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
1100  if( cur ) {
1101  any_part_added = mrmimeparser_parse_mime_recursive(ths, (struct mailmime*)clist_content(cur));
1102  }
1103  break;
1104 
1105  case MR_MIMETYPE_MP_NOT_DECRYPTABLE:
1106  {
1107  mrmimepart_t* part = mrmimepart_new();
1108  part->m_type = MR_MSG_TEXT;
1109  part->m_msg = mrstock_str(MR_STR_ENCRYPTEDMSG); /* not sure if the text "Encrypted message" is 100% sufficient here (bp) */
1110  carray_add(ths->m_parts, (void*)part, NULL);
1111  any_part_added = 1;
1112  ths->m_decrypting_failed = 1;
1113  }
1114  break;
1115 
1116  case MR_MIMETYPE_MP_SIGNED:
1117  /* RFC 1847: "The multipart/signed content type contains exactly two body parts.
1118  The first body part is the body part over which the digital signature was created [...]
1119  The second body part contains the control information necessary to verify the digital signature."
1120  We simpliy take the first body part and skip the rest.
1121  (see https://k9mail.github.io/2016/11/24/OpenPGP-Considerations-Part-I.html for background information why we use encrypted+signed) */
1122  if( (cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list)) != NULL )
1123  {
1124  any_part_added = mrmimeparser_parse_mime_recursive(ths, (struct mailmime*)clist_content(cur));
1125  }
1126  break;
1127 
1128  case MR_MIMETYPE_MP_REPORT:
1129  if( clist_count(mime->mm_data.mm_multipart.mm_mp_list) >= 2 ) /* RFC 6522: the first part is for humans, the second for machines */
1130  {
1131  struct mailmime_parameter* report_type = mr_find_ct_parameter(mime, "report-type");
1132  if( report_type && report_type->pa_value
1133  && strcmp(report_type->pa_value, "disposition-notification") == 0 )
1134  {
1135  carray_add(ths->m_reports, (void*)mime, NULL);
1136  }
1137  else
1138  {
1139  /* eg. `report-type=delivery-status`; maybe we should show them as a little error icon */
1140  any_part_added = mrmimeparser_parse_mime_recursive(ths, (struct mailmime*)clist_content(clist_begin(mime->mm_data.mm_multipart.mm_mp_list)));
1141  }
1142  }
1143  break;
1144 
1145  default: /* eg. MR_MIME_MP_MIXED - add all parts (in fact, AddSinglePartIfKnown() later check if the parts are really supported) */
1146  {
1147  /* HACK: the following lines are a hack for clients who use multipart/mixed instead of multipart/alternative for
1148  combined text/html messages (eg. Stock Android "Mail" does so). So, if I detect such a message below, I skip the HTML part.
1149  However, I'm not sure, if there are useful situations to use plain+html in multipart/mixed - if so, we should disable the hack. */
1150  struct mailmime* skip_part = NULL;
1151  {
1152  struct mailmime* html_part = NULL;
1153  int plain_cnt = 0, html_cnt = 0;
1154  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
1155  struct mailmime* childmime = (struct mailmime*)clist_content(cur);
1156  if( mrmimeparser_get_mime_type(childmime, NULL) == MR_MIMETYPE_TEXT_PLAIN ) {
1157  plain_cnt++;
1158  }
1159  else if( mrmimeparser_get_mime_type(childmime, NULL) == MR_MIMETYPE_TEXT_HTML ) {
1160  html_part = childmime;
1161  html_cnt++;
1162  }
1163  }
1164  if( plain_cnt==1 && html_cnt==1 ) {
1165  mrmailbox_log_warning(ths->m_mailbox, 0, "HACK: multipart/mixed message found with PLAIN and HTML, we'll skip the HTML part as this seems to be unwanted.");
1166  skip_part = html_part;
1167  }
1168  }
1169  /* /HACK */
1170 
1171  for( cur=clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur!=NULL; cur=clist_next(cur)) {
1172  struct mailmime* childmime = (struct mailmime*)clist_content(cur);
1173  if( childmime != skip_part ) {
1174  if( mrmimeparser_parse_mime_recursive(ths, childmime) ) {
1175  any_part_added = 1;
1176  }
1177  }
1178  }
1179  }
1180  break;
1181  }
1182  break;
1183 
1184  case MAILMIME_MESSAGE:
1185  if( ths->m_header == NULL && mime->mm_data.mm_message.mm_fields )
1186  {
1187  ths->m_header = mime->mm_data.mm_message.mm_fields;
1188  for( cur = clist_begin(ths->m_header->fld_list); cur!=NULL ; cur=clist_next(cur) ) {
1189  struct mailimf_field* field = (struct mailimf_field*)clist_content(cur);
1190  if( field->fld_type == MAILIMF_FIELD_SUBJECT ) {
1191  if( ths->m_subject == NULL && field->fld_data.fld_subject ) {
1192  ths->m_subject = mr_decode_header_string(field->fld_data.fld_subject->sbj_value);
1193  }
1194  }
1195  else if( field->fld_type == MAILIMF_FIELD_OPTIONAL_FIELD ) {
1196  struct mailimf_optional_field* optional_field = field->fld_data.fld_optional_field;
1197  if( optional_field ) {
1198  if( strcasecmp(optional_field->fld_name, "X-MrMsg")==0 || strcasecmp(optional_field->fld_name, "Chat-Version")==0 ) {
1199  ths->m_is_send_by_messenger = 1;
1200  }
1201  }
1202  }
1203  }
1204  }
1205 
1206  if( mime->mm_data.mm_message.mm_msg_mime )
1207  {
1208  any_part_added = mrmimeparser_parse_mime_recursive(ths, mime->mm_data.mm_message.mm_msg_mime);
1209  }
1210  break;
1211  }
1212 
1213  return any_part_added;
1214 }
1215 
1216 
1217 static struct mailimf_optional_field* mrmimeparser_find_xtra_field(mrmimeparser_t* ths, const char* wanted_fld_name)
1218 {
1219  return mr_find_mailimf_field2(ths->m_header, wanted_fld_name);
1220 }
1221 
1222 
1223 void mrmimeparser_parse(mrmimeparser_t* ths, const char* body_not_terminated, size_t body_bytes)
1224 {
1225  int r;
1226  size_t index = 0;
1227 
1228  mrmimeparser_empty(ths);
1229 
1230  /* parse body */
1231  r = mailmime_parse(body_not_terminated, body_bytes, &index, &ths->m_mimeroot);
1232  if(r != MAILIMF_NO_ERROR || ths->m_mimeroot == NULL ) {
1233  goto cleanup;
1234  }
1235 
1236  #if 0
1237  printf("-----------------------------------------------------------------------\n");
1238  mr_print_mime(m_mimeroot);
1239  printf("-----------------------------------------------------------------------\n");
1240  #endif
1241 
1242  /* decrypt, if possible; handle Autocrypt:-header
1243  (decryption may modifiy the given object) */
1244  int validation_errors = 0;
1245  if( mrmailbox_e2ee_decrypt(ths->m_mailbox, ths->m_mimeroot, &validation_errors) ) {
1246  if( validation_errors == 0 ) {
1247  ths->m_decrypted_and_validated = 1;
1248  }
1249  else {
1250  ths->m_decrypted_with_validation_errors = validation_errors;
1251  }
1252  }
1253 
1254  /* recursively check, whats parsed */
1255  mrmimeparser_parse_mime_recursive(ths, ths->m_mimeroot);
1256 
1257  /* prepend subject to message? */
1258  if( ths->m_subject )
1259  {
1260  int prepend_subject = 1;
1261  if( !ths->m_decrypting_failed /* if decryption has failed, we always prepend the subject as this may contain cleartext hints from non-Delta MUAs. */ )
1262  {
1263  char* p = strchr(ths->m_subject, ':');
1264  if( (p-ths->m_subject) == 2 /*Re: etc.*/
1265  || (p-ths->m_subject) == 3 /*Fwd: etc.*/
1266  || ths->m_is_send_by_messenger
1267  || strstr(ths->m_subject, MR_CHAT_PREFIX)!=NULL ) {
1268  prepend_subject = 0;
1269  }
1270  }
1271 
1272  if( prepend_subject )
1273  {
1274  char* subj = safe_strdup(ths->m_subject);
1275  char* p = strchr(subj, '['); /* do not add any tags as "[checked by XYZ]" */
1276  if( p ) {
1277  *p = 0;
1278  }
1279  mr_trim(subj);
1280  if( subj[0] ) {
1281  int i, icnt = carray_count(ths->m_parts); /* should be at least one - maybe empty - part */
1282  for( i = 0; i < icnt; i++ ) {
1283  mrmimepart_t* part = (mrmimepart_t*)carray_get(ths->m_parts, i);
1284  if( part->m_type == MR_MSG_TEXT ) {
1285  #define MR_NDASH "\xE2\x80\x93"
1286  char* new_txt = mr_mprintf("%s " MR_NDASH " %s", subj, part->m_msg);
1287  free(part->m_msg);
1288  part->m_msg = new_txt;
1289  break;
1290  }
1291  }
1292  }
1293  free(subj);
1294  }
1295  }
1296 
1297  /* add forward information to every part */
1298  if( ths->m_is_forwarded ) {
1299  int i, icnt = carray_count(ths->m_parts); /* should be at least one - maybe empty - part */
1300  for( i = 0; i < icnt; i++ ) {
1301  mrmimepart_t* part = (mrmimepart_t*)carray_get(ths->m_parts, i);
1302  mrparam_set_int(part->m_param, MRP_FORWARDED, 1);
1303  }
1304  }
1305 
1306  if( carray_count(ths->m_parts)==1 )
1307  {
1308  /* mark audio as voice message, if appropriate (we have to do this on global level as we do not know the global header in the recursice parse).
1309  and read some additional parameters */
1310  mrmimepart_t* part = (mrmimepart_t*)carray_get(ths->m_parts, 0);
1311  if( part->m_type == MR_MSG_AUDIO ) {
1312  if( mrmimeparser_find_xtra_field(ths, "X-MrVoiceMessage") || mrmimeparser_find_xtra_field(ths, "Chat-Voice-Message") ) {
1313  free(part->m_msg);
1314  part->m_msg = strdup("ogg"); /* MR_MSG_AUDIO adds sets the whole filename which is useless. however, the extension is useful. */
1315  part->m_type = MR_MSG_VOICE;
1316  mrparam_set(part->m_param, MRP_AUTHORNAME, NULL); /* remove unneeded information */
1317  mrparam_set(part->m_param, MRP_TRACKNAME, NULL);
1318  }
1319  }
1320 
1321  if( part->m_type == MR_MSG_AUDIO || part->m_type == MR_MSG_VOICE || part->m_type == MR_MSG_VIDEO ) {
1322  const struct mailimf_optional_field* field = mrmimeparser_find_xtra_field(ths, "X-MrDurationMs");
1323  if( field==NULL ) { field = mrmimeparser_find_xtra_field(ths, "Chat-Duration"); }
1324  if( field ) {
1325  int duration_ms = atoi(field->fld_value);
1326  if( duration_ms > 0 && duration_ms < 24*60*60*1000 ) {
1327  mrparam_set_int(part->m_param, MRP_DURATION, duration_ms);
1328  }
1329  }
1330  }
1331  }
1332 
1333  /* some special system message? */
1334  if( mrmimeparser_find_xtra_field(ths, "Chat-Group-Image")
1335  && carray_count(ths->m_parts)>=1 ) {
1336  mrmimepart_t* textpart = (mrmimepart_t*)carray_get(ths->m_parts, 0);
1337  if( textpart->m_type == MR_MSG_TEXT ) {
1338  mrparam_set_int(textpart->m_param, MRP_SYSTEM_CMD, MR_SYSTEM_GROUPIMAGE_CHANGED);
1339  if( carray_count(ths->m_parts)>=2 ) {
1340  mrmimepart_t* imgpart = (mrmimepart_t*)carray_get(ths->m_parts, 1);
1341  if( imgpart->m_type == MR_MSG_IMAGE ) {
1342  imgpart->m_is_meta = 1;
1343  }
1344  }
1345  }
1346  }
1347 
1348  /* check, if the message asks for a MDN */
1349  if( !ths->m_decrypting_failed )
1350  {
1351  const struct mailimf_optional_field* dn_field = mrmimeparser_find_xtra_field(ths, "Chat-Disposition-Notification-To"); /* we use "Chat-Disposition-Notification-To" as replies to "Disposition-Notification-To" are weired in many cases, are just freetext and/or do not follow any standard. */
1352  if( dn_field && mrmimeparser_get_last_nonmeta(ths)/*just check if the mail is not empty*/ )
1353  {
1354  struct mailimf_mailbox_list* mb_list = NULL;
1355  size_t index = 0;
1356  if( mailimf_mailbox_list_parse(dn_field->fld_value, strlen(dn_field->fld_value), &index, &mb_list)==MAILIMF_NO_ERROR && mb_list )
1357  {
1358  char* dn_to_addr = mr_find_first_addr(mb_list);
1359  if( dn_to_addr )
1360  {
1361  struct mailimf_field* from_field = mr_find_mailimf_field(ths->m_header, MAILIMF_FIELD_FROM); /* we need From: as this MUST match Disposition-Notification-To: */
1362  if( from_field && from_field->fld_data.fld_from )
1363  {
1364  char* from_addr = mr_find_first_addr(from_field->fld_data.fld_from->frm_mb_list);
1365  if( from_addr )
1366  {
1367  if( strcmp(from_addr, dn_to_addr)==0 )
1368  {
1369  /* we mark _only_ the _last_ part to send a MDN
1370  (this avoids trouble with multi-part-messages who should send only one MDN.
1371  Moreover the last one is handy as it is the one typically displayed if the message is larger) */
1372  mrmimepart_t* part = mrmimeparser_get_last_nonmeta(ths);
1373  if( part ) {
1374  mrparam_set_int(part->m_param, MRP_WANTS_MDN, 1);
1375  }
1376  }
1377  free(from_addr);
1378  }
1379  }
1380  free(dn_to_addr);
1381  }
1382  mailimf_mailbox_list_free(mb_list);
1383  }
1384  }
1385  }
1386 
1387  /* Cleanup - and try to create at least an empty part if there are no parts yet */
1388 cleanup:
1389  if( !mrmimeparser_has_nonmeta(ths) && carray_count(ths->m_reports)==0 ) {
1390  mrmimepart_t* part = mrmimepart_new();
1391  part->m_type = MR_MSG_TEXT;
1392  part->m_msg = safe_strdup(ths->m_subject? ths->m_subject : "Empty message");
1393  carray_add(ths->m_parts, (void*)part, NULL);
1394  }
1395 }
1396 
1397 
1398 mrmimepart_t* mrmimeparser_get_last_nonmeta(mrmimeparser_t* ths)
1399 {
1400  if( ths && ths->m_parts ) {
1401  int i, icnt = carray_count(ths->m_parts);
1402  for( i = icnt-1; i >= 0; i-- ) {
1403  mrmimepart_t* part = (mrmimepart_t*)carray_get(ths->m_parts, i);
1404  if( part && !part->m_is_meta ) {
1405  return part;
1406  }
1407  }
1408  }
1409  return NULL;
1410 }
1411 
1412 
1413 int mrmimeparser_is_mailinglist_message(mrmimeparser_t* ths)
1414 {
1415  /* the function checks if the header of the mail looks as if it is a message from a mailing list
1416 
1417  Some statistics:
1418  => sorted out by `List-ID`-header:
1419  - Mailman mailing list messages - OK, mass messages
1420  - Xing forum/event notifications - OK, mass messages
1421  - Xing welcome-back, contact-reqest - Hm, but it _has_ the List-ID header
1422 
1423  => sorted out by `Precedence`-header:
1424  - Majordomo mailing list messages - OK, mass messages
1425 
1426  => NOT sorted out:
1427  - Pingdom notifications - OK, individual message
1428  - Paypal notifications - OK, individual message
1429  - Linked in visits, do-you-know - OK, individual message
1430  - Share-It notifications - OK, individual message
1431  - Transifex, Github notifications - OK, individual message
1432  */
1433 
1434  if( ths == NULL ) {
1435  return 0;
1436  }
1437 
1438  if( mr_find_mailimf_field2(ths->m_header, "List-Id") != NULL ) {
1439  return 1; /* mailing list identified by the presence of `List-ID` from RFC 2919 */
1440  }
1441 
1442  struct mailimf_optional_field* precedence = mr_find_mailimf_field2(ths->m_header, "Precedence");
1443  if( precedence != NULL ) {
1444  if( strcasecmp(precedence->fld_value, "list")==0
1445  || strcasecmp(precedence->fld_value, "bulk")==0 ) {
1446  return 1; /* mailing list identified by the presence of `Precedence: bulk` or `Precedence: list` from RFC 3834 */
1447  }
1448  }
1449 
1450  return 0;
1451 }
1452 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
-
void mrparam_set(mrparam_t *param, int key, const char *value)
Set parameter to a string.
Definition: mrparam.c:253
-
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
-
void mrparam_set_int(mrparam_t *param, int key, int32_t value)
Set parameter to an integer.
Definition: mrparam.c:318
-
- - - - diff --git a/docs/user/html/mrmsg_8c_source.html b/docs/user/html/mrmsg_8c_source.html deleted file mode 100644 index 23fe3825..00000000 --- a/docs/user/html/mrmsg_8c_source.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrmsg.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrmsg.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 #include "mrimap.h"
25 #include "mrsmtp.h"
26 #include "mrjob.h"
27 #include "mrpgp.h"
28 #include "mrmimefactory.h"
29 
30 
42 {
43  mrmsg_t* ths = NULL;
44 
45  if( (ths=calloc(1, sizeof(mrmsg_t)))==NULL ) {
46  exit(15); /* cannot allocate little memory, unrecoverable error */
47  }
48 
49  ths->m_type = MR_MSG_UNDEFINED;
50  ths->m_state = MR_STATE_UNDEFINED;
51  ths->m_param = mrparam_new();
52 
53  return ths;
54 }
55 
56 
66 void mrmsg_unref(mrmsg_t* msg)
67 {
68  if( msg==NULL ) {
69  return;
70  }
71 
72  mrmsg_empty(msg);
73  mrparam_unref(msg->m_param);
74  free(msg);
75 }
76 
77 
87 void mrmsg_empty(mrmsg_t* msg)
88 {
89  if( msg == NULL ) {
90  return;
91  }
92 
93  free(msg->m_text);
94  msg->m_text = NULL;
95 
96  free(msg->m_rfc724_mid);
97  msg->m_rfc724_mid = NULL;
98 
99  free(msg->m_server_folder);
100  msg->m_server_folder = NULL;
101 
102  mrparam_set_packed(msg->m_param, NULL);
103 
104  msg->m_mailbox = NULL;
105 }
106 
107 
125 void mrmsg_set_text(mrmsg_t* msg, const char* text)
126 {
127  if( msg==NULL || text==NULL ) {
128  return;
129  }
130 
131  free(msg->m_text);
132  msg->m_text = safe_strdup(text);
133 }
134 
135 
149 void mrmsg_guess_msgtype_from_suffix(const char* pathNfilename, int* ret_msgtype, char** ret_mime)
150 {
151  if( pathNfilename == NULL || ret_msgtype == NULL || ret_mime == NULL) {
152  return;
153  }
154 
155  *ret_msgtype = MR_MSG_UNDEFINED;
156  *ret_mime = NULL;
157 
158  char* s = mr_get_filesuffix_lc(pathNfilename);
159  if( s == NULL ) {
160  goto cleanup;
161  }
162 
163  if( strcmp(s, "mp3")==0 ) {
164  *ret_msgtype = MR_MSG_AUDIO;
165  *ret_mime = safe_strdup("audio/mpeg");
166  }
167  else if( strcmp(s, "mp4")==0 ) {
168  *ret_msgtype = MR_MSG_VIDEO;
169  *ret_mime = safe_strdup("video/mp4");
170  }
171  else if( strcmp(s, "jpg")==0 || strcmp(s, "jpeg")==0 ) {
172  *ret_msgtype = MR_MSG_IMAGE;
173  *ret_mime = safe_strdup("image/jpeg");
174  }
175  else if( strcmp(s, "png")==0 ) {
176  *ret_msgtype = MR_MSG_IMAGE;
177  *ret_mime = safe_strdup("image/png");
178  }
179  else if( strcmp(s, "gif")==0 ) {
180  *ret_msgtype = MR_MSG_GIF;
181  *ret_mime = safe_strdup("image/gif");
182  }
183 
184 cleanup:
185  free(s);
186 }
187 
188 
189 int mrmsg_set_from_stmt__(mrmsg_t* ths, sqlite3_stmt* row, int row_offset) /* field order must be MR_MSG_FIELDS */
190 {
191  mrmsg_empty(ths);
192 
193  ths->m_id = (uint32_t)sqlite3_column_int (row, row_offset++);
194  ths->m_rfc724_mid = safe_strdup((char*)sqlite3_column_text (row, row_offset++));
195  ths->m_server_folder= safe_strdup((char*)sqlite3_column_text (row, row_offset++));
196  ths->m_server_uid = (uint32_t)sqlite3_column_int (row, row_offset++);
197  ths->m_chat_id = (uint32_t)sqlite3_column_int (row, row_offset++);
198 
199  ths->m_from_id = (uint32_t)sqlite3_column_int (row, row_offset++);
200  ths->m_to_id = (uint32_t)sqlite3_column_int (row, row_offset++);
201  ths->m_timestamp = (time_t)sqlite3_column_int64(row, row_offset++);
202 
203  ths->m_type = sqlite3_column_int (row, row_offset++);
204  ths->m_state = sqlite3_column_int (row, row_offset++);
205  ths->m_is_msgrmsg = sqlite3_column_int (row, row_offset++);
206  ths->m_text = safe_strdup((char*)sqlite3_column_text (row, row_offset++));
207 
208  mrparam_set_packed( ths->m_param, (char*)sqlite3_column_text (row, row_offset++));
209  ths->m_starred = sqlite3_column_int (row, row_offset++);
210 
211  if( ths->m_chat_id == MR_CHAT_ID_DEADDROP ) {
212  mr_truncate_n_unwrap_str(ths->m_text, 256, 0); /* 256 characters is about a half screen on a 5" smartphone display */
213  }
214 
215  return 1;
216 }
217 
218 
226 int mrmsg_load_from_db__(mrmsg_t* ths, mrmailbox_t* mailbox, uint32_t id)
227 {
228  sqlite3_stmt* stmt;
229 
230  if( ths==NULL || mailbox==NULL || mailbox->m_sql==NULL ) {
231  return 0;
232  }
233 
234  stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_ircftttstpb_FROM_msg_WHERE_i,
235  "SELECT " MR_MSG_FIELDS " FROM msgs m WHERE m.id=?;");
236  sqlite3_bind_int(stmt, 1, id);
237 
238  if( sqlite3_step(stmt) != SQLITE_ROW ) {
239  return 0;
240  }
241 
242  if( !mrmsg_set_from_stmt__(ths, stmt, 0) ) { /* also calls mrmsg_empty() */
243  return 0;
244  }
245 
246  ths->m_mailbox = mailbox;
247 
248  return 1;
249 }
250 
251 
264 {
265  mrpoortext_t* ret = mrpoortext_new();
266  mrcontact_t* contact = NULL;
267  mrchat_t* chat_to_delete = NULL;
268 
269  if( msg==NULL ) {
270  goto cleanup;
271  }
272 
273  if( chat == NULL ) {
274  if( (chat=mrmailbox_get_chat(msg->m_mailbox, msg->m_chat_id)) == NULL ) {
275  goto cleanup;
276  }
277  chat_to_delete = chat;
278  }
279 
280  if( msg->m_from_id != MR_CONTACT_ID_SELF && chat->m_type == MR_CHAT_TYPE_GROUP ) {
281  contact = mrmailbox_get_contact(chat->m_mailbox, msg->m_from_id);
282  }
283 
284  mrpoortext_fill(ret, msg, chat, contact);
285 
286 cleanup:
287  mrcontact_unref(contact);
288  mrchat_unref(chat_to_delete);
289  return ret;
290 }
291 
292 
303 {
304  /* a padlock guarantees that the message is e2ee _and_ answers will be as well */
305  if( msg != NULL ) {
306  if( msg->m_mailbox && msg->m_mailbox->m_e2ee_enabled ) {
307  if( mrparam_get_int(msg->m_param, MRP_GUARANTEE_E2EE, 0) != 0 ) {
308  return 1;
309  }
310  }
311  }
312  return 0;
313 }
314 
315 
316 void mrmsg_get_authorNtitle_from_filename(const char* pathNfilename, char** ret_author, char** ret_title)
317 {
318  /* function extracts AUTHOR and TITLE from a path given as `/path/other folder/AUTHOR - TITLE.mp3`
319  if the mark ` - ` is not preset, the whole name (without suffix) is used as the title and the author is NULL. */
320  char *author = NULL, *title = NULL, *p;
321  mr_split_filename(pathNfilename, &title, NULL);
322  p = strstr(title, " - ");
323  if( p ) {
324  *p = 0;
325  author = title;
326  title = safe_strdup(&p[3]);
327  }
328 
329  if( ret_author ) { *ret_author = author; } else { free(author); }
330  if( ret_title ) { *ret_title = title; } else { free(title); }
331 }
332 
333 
340 char* mrmsg_get_summarytext(mrmsg_t* msg, int approx_characters)
341 {
342  if( msg==NULL ) {
343  return safe_strdup(NULL);
344  }
345 
346  return mrmsg_get_summarytext_by_raw(msg->m_type, msg->m_text, msg->m_param, approx_characters);
347 }
348 
349 
350 char* mrmsg_get_summarytext_by_raw(int type, const char* text, mrparam_t* param, int approx_characters)
351 {
352  char* ret = NULL;
353  char* pathNfilename = NULL, *label = NULL, *value = NULL;
354 
355  switch( type ) {
356  case MR_MSG_IMAGE:
357  ret = mrstock_str(MR_STR_IMAGE);
358  break;
359 
360  case MR_MSG_GIF:
361  ret = mrstock_str(MR_STR_GIF);
362  break;
363 
364  case MR_MSG_VIDEO:
365  ret = mrstock_str(MR_STR_VIDEO);
366  break;
367 
368  case MR_MSG_VOICE:
369  ret = mrstock_str(MR_STR_VOICEMESSAGE);
370  break;
371 
372  case MR_MSG_AUDIO:
373  if( (value=mrparam_get(param, MRP_TRACKNAME, NULL))==NULL ) { /* although we send files with "author - title" in the filename, existing files may follow other conventions, so this lookup is neccessary */
374  pathNfilename = mrparam_get(param, MRP_FILE, "ErrFilename");
375  mrmsg_get_authorNtitle_from_filename(pathNfilename, NULL, &value);
376  }
377  label = mrstock_str(MR_STR_AUDIO);
378  ret = mr_mprintf("%s: %s", label, value);
379  break;
380 
381  case MR_MSG_FILE:
382  pathNfilename = mrparam_get(param, MRP_FILE, "ErrFilename");
383  value = mr_get_filename(pathNfilename);
384  label = mrstock_str(MR_STR_FILE);
385  ret = mr_mprintf("%s: %s", label, value);
386  break;
387 
388  default:
389  if( text ) {
390  ret = safe_strdup(text);
391  mr_truncate_n_unwrap_str(ret, approx_characters, 1);
392  }
393  break;
394  }
395 
396  /* cleanup */
397  free(pathNfilename);
398  free(label);
399  free(value);
400  if( ret == NULL ) {
401  ret = safe_strdup(NULL);
402  }
403  return ret;
404 }
405 
406 
417 char* mrmsg_get_fullpath(mrmsg_t* msg)
418 {
419  char* ret = NULL;
420 
421  if( msg == NULL ) {
422  goto cleanup;
423  }
424 
425  ret = mrparam_get(msg->m_param, MRP_FILE, NULL);
426 
427 cleanup:
428  return ret? ret : safe_strdup(NULL);
429 }
430 
431 
442 char* mrmsg_get_filename(mrmsg_t* msg)
443 {
444  char* ret = NULL, *pathNfilename = NULL;
445 
446  if( msg == NULL ) {
447  goto cleanup;
448  }
449 
450  pathNfilename = mrparam_get(msg->m_param, MRP_FILE, NULL);
451  if( pathNfilename == NULL ) {
452  goto cleanup;
453  }
454 
455  ret = mr_get_filename(pathNfilename);
456 
457 cleanup:
458  free(pathNfilename);
459  return ret? ret : safe_strdup(NULL);
460 }
461 
462 
481 {
482  mrpoortext_t* ret = mrpoortext_new();
483  char *pathNfilename = NULL;
484  mrcontact_t* contact = NULL;
485 
486  if( msg == NULL || msg->m_mailbox == NULL ) {
487  goto cleanup;
488  }
489 
490  if( msg->m_type == MR_MSG_VOICE )
491  {
492  if( (contact = mrmailbox_get_contact(msg->m_mailbox, msg->m_from_id))==NULL ) {
493  goto cleanup;
494  }
495  ret->m_text1 = safe_strdup((contact->m_name&&contact->m_name[0])? contact->m_name : contact->m_addr);
496  ret->m_text2 = mrstock_str(MR_STR_VOICEMESSAGE);
497  }
498  else
499  {
500  ret->m_text1 = mrparam_get(msg->m_param, MRP_AUTHORNAME, NULL);
501  ret->m_text2 = mrparam_get(msg->m_param, MRP_TRACKNAME, NULL);
502  if( ret->m_text1 && ret->m_text1[0] && ret->m_text2 && ret->m_text2[0] ) {
503  goto cleanup;
504  }
505  free(ret->m_text1); ret->m_text1 = NULL;
506  free(ret->m_text2); ret->m_text2 = NULL;
507 
508  pathNfilename = mrparam_get(msg->m_param, MRP_FILE, NULL);
509  if( pathNfilename == NULL ) {
510  goto cleanup;
511  }
512  mrmsg_get_authorNtitle_from_filename(pathNfilename, &ret->m_text1, &ret->m_text2);
513  if( ret->m_text1 == NULL && ret->m_text2 != NULL ) {
514  ret->m_text1 = mrstock_str(MR_STR_AUDIO);
515  }
516  }
517 
518 cleanup:
519  free(pathNfilename);
520  mrcontact_unref(contact);
521  return ret;
522 }
523 
524 
525 int mrmsg_is_increation__(const mrmsg_t* msg)
526 {
527  int is_increation = 0;
528  if( MR_MSG_NEEDS_ATTACHMENT(msg->m_type) )
529  {
530  char* pathNfilename = mrparam_get(msg->m_param, MRP_FILE, NULL);
531  if( pathNfilename ) {
532  char* totest = mr_mprintf("%s.increation", pathNfilename);
533  if( mr_file_exist(totest) ) {
534  is_increation = 1;
535  }
536  free(totest);
537  free(pathNfilename);
538  }
539  }
540  return is_increation;
541 }
542 
543 
555 int mrmsg_is_increation(mrmsg_t* msg)
556 {
557  /* surrounds mrmsg_is_increation__() with locking and error checking */
558  int is_increation = 0;
559  if( msg && msg->m_mailbox && MR_MSG_NEEDS_ATTACHMENT(msg->m_type) /*additional check for speed reasons*/ )
560  {
561  mrsqlite3_lock(msg->m_mailbox->m_sql);
562  is_increation = mrmsg_is_increation__(msg);
563  mrsqlite3_unlock(msg->m_mailbox->m_sql);
564  }
565  return is_increation;
566 }
567 
568 
569 /* Internal function similar to mrmsg_save_param_to_disk() but without locking. */
570 void mrmsg_save_param_to_disk__(mrmsg_t* msg)
571 {
572  if( msg == NULL || msg->m_mailbox == NULL || msg->m_mailbox->m_sql == NULL ) {
573  return;
574  }
575 
576  sqlite3_stmt* stmt = mrsqlite3_predefine__(msg->m_mailbox->m_sql, UPDATE_msgs_SET_param_WHERE_id,
577  "UPDATE msgs SET param=? WHERE id=?;");
578  sqlite3_bind_text(stmt, 1, msg->m_param->m_packed, -1, SQLITE_STATIC);
579  sqlite3_bind_int (stmt, 2, msg->m_id);
580  sqlite3_step(stmt);
581 }
582 
583 
591 {
592  if( msg == NULL || msg->m_mailbox == NULL || msg->m_mailbox->m_sql == NULL ) {
593  return;
594  }
595 
596  mrsqlite3_lock(msg->m_mailbox->m_sql);
597  mrmsg_save_param_to_disk__(msg);
598  mrsqlite3_unlock(msg->m_mailbox->m_sql);
599 }
600 
int m_is_msgrmsg
Set to 1 if the message was sent by another messenger.
Definition: mrmsg.h:75
-
mrchat_t * mrmailbox_get_chat(mrmailbox_t *mailbox, uint32_t chat_id)
Get a chat object of type mrchat_t by a chat_id.
Definition: mrmailbox.c:1709
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
char * m_text2
may be NULL
Definition: mrpoortext.h:39
-
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
void mrchat_unref(mrchat_t *chat)
Free a chat object.
Definition: mrchat.c:160
-
int m_type
Message type as one of the MR_MSG_* contstants.
Definition: mrmsg.h:60
-
char * mrmsg_get_summarytext(mrmsg_t *msg, int approx_characters)
Get a message summary as a single line of text.
Definition: mrmsg.c:340
-
uint32_t m_chat_id
Chat ID the message belongs to.
Definition: mrmsg.h:49
-
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
-
mrmsg_t * mrmsg_new()
Create new message object.
Definition: mrmsg.c:41
-
char * m_text1
may be NULL
Definition: mrpoortext.h:38
-
void mrcontact_unref(mrcontact_t *ths)
Free a contact object.
Definition: mrcontact.c:49
-
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
-
char * mrparam_get(mrparam_t *param, int key, const char *def)
Get value of a parameter.
Definition: mrparam.c:186
-
mrpoortext_t * mrmsg_get_summary(mrmsg_t *msg, mrchat_t *chat)
Get a summary for a message.
Definition: mrmsg.c:263
-
mrpoortext_t * mrmsg_get_mediainfo(mrmsg_t *msg)
Get real author and title.
Definition: mrmsg.c:480
-
mrmailbox_t * m_mailbox
!= NULL
Definition: mrchat.h:58
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
int mrmsg_show_padlock(mrmsg_t *msg)
Check if a padlock should be shown beside the message.
Definition: mrmsg.c:302
-
uint32_t m_to_id
Contact ID of the receiver, if appropriate.
Definition: mrmsg.h:48
-
uint32_t m_from_id
Contact ID of the sender.
Definition: mrmsg.h:47
-
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
-
int m_starred
Starred-state of the message.
Definition: mrmsg.h:74
-
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
-
void mrmsg_set_text(mrmsg_t *msg, const char *text)
Set the text of a message object.
Definition: mrmsg.c:125
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
-
mrparam_t * m_param
MRP_FILE, MRP_WIDTH, MRP_HEIGHT etc.
Definition: mrmsg.h:73
-
char * m_text
message text or NULL if unset
Definition: mrmsg.h:72
-
void mrmsg_unref(mrmsg_t *msg)
Free an mrmsg_t object created eg.
Definition: mrmsg.c:66
-
uint32_t m_id
Message ID.
Definition: mrmsg.h:45
-
int m_state
Message state as one of the MR_MSG_STATE_* contstants.
Definition: mrmsg.h:70
-
void mrmsg_save_param_to_disk(mrmsg_t *msg)
can be used to add some additional, persistent information to a messages record.
Definition: mrmsg.c:590
-
mrcontact_t * mrmailbox_get_contact(mrmailbox_t *mailbox, uint32_t contact_id)
Get a single contact object.
Definition: mrmailbox.c:4216
-
void mrmsg_empty(mrmsg_t *msg)
Empty a message object.
Definition: mrmsg.c:87
-
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
-
char * m_name
may be NULL or empty, this name should not be spreaded as it may be "Daddy" and so on; initially set ...
Definition: mrcontact.h:45
-
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
- - - - diff --git a/docs/user/html/mrosnative_8c_source.html b/docs/user/html/mrosnative_8c_source.html deleted file mode 100644 index c2114725..00000000 --- a/docs/user/html/mrosnative_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrosnative.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrosnative.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Some functions that are called by the backend under certain
24 circumstances. The frontents should create a copy of this file
25 and implement the functions as needed, eg. for attaching threads in JNI. */
26 
27 
28 #include <stdlib.h>
29 #include "mrmailbox.h"
30 #include "mrosnative.h"
31 
32 
33 int mrosnative_setup_thread(mrmailbox_t* mailbox)
34 {
35  return 1;
36 }
37 
38 
39 void mrosnative_unsetup_thread(mrmailbox_t* mailbox)
40 {
41 }
42 
43 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrparam_8c_source.html b/docs/user/html/mrparam_8c_source.html deleted file mode 100644 index dac7f4bb..00000000 --- a/docs/user/html/mrparam_8c_source.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrparam.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrparam.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include "mrmailbox_internal.h"
27 #include "mrtools.h"
28 
29 
30 static char* find_param(char* ths, int key, char** ret_p2)
31 {
32  char *p1, *p2;
33 
34  /* let p1 point to the start of the */
35  p1 = ths;
36  while( 1 ) {
37  if( p1 == NULL || *p1 == 0 ) {
38  return NULL;
39  }
40  else if( *p1 == key && p1[1] == '=' ) {
41  break;
42  }
43  else {
44  p1 = strchr(p1, '\n'); /* if `\r\n` is used, this `\r` is also skipped by this*/
45  if( p1 ) {
46  p1++;
47  }
48  }
49  }
50 
51  /* let p2 point to the character _after_ the value - eiter `\n` or `\0` */
52  p2 = strchr(p1, '\n');
53  if( p2 == NULL ) {
54  p2 = &p1[strlen(p1)];
55  }
56 
57  *ret_p2 = p2;
58  return p1;
59 }
60 
61 
70 {
71  mrparam_t* param;
72 
73  if( (param=calloc(1, sizeof(mrparam_t)))==NULL ) {
74  exit(28); /* cannot allocate little memory, unrecoverable error */
75  }
76 
77  param->m_packed = calloc(1, 1);
78 
79  return param;
80 }
81 
82 
91 {
92  if( param==NULL ) {
93  return;
94  }
95 
96  mrparam_empty(param);
97  free(param->m_packed);
98  free(param);
99 }
100 
101 
112 {
113  if( param == NULL ) {
114  return;
115  }
116 
117  param->m_packed[0] = 0;
118 }
119 
120 
135 void mrparam_set_packed(mrparam_t* param, const char* packed)
136 {
137  if( param == NULL ) {
138  return;
139  }
140 
141  mrparam_empty(param);
142 
143  if( packed ) {
144  free(param->m_packed);
145  param->m_packed = safe_strdup(packed);
146  }
147 }
148 
149 
161 int mrparam_exists(mrparam_t* param, int key)
162 {
163  char *p2;
164 
165  if( param == NULL || key == 0 ) {
166  return 0;
167  }
168 
169  return find_param(param->m_packed, key, &p2)? 1 : 0;
170 }
171 
172 
186 char* mrparam_get(mrparam_t* param, int key, const char* def)
187 {
188  char *p1, *p2, bak, *ret;
189 
190  if( param == NULL || key == 0 ) {
191  return def? safe_strdup(def) : NULL;
192  }
193 
194  p1 = find_param(param->m_packed, key, &p2);
195  if( p1 == NULL ) {
196  return def? safe_strdup(def) : NULL;
197  }
198 
199  p1 += 2; /* skip key and "=" (safe as find_param checks for its existance) */
200 
201  bak = *p2;
202  *p2 = 0;
203  ret = safe_strdup(p1);
204  mr_rtrim(ret); /* to be safe with '\r' characters ... */
205  *p2 = bak;
206  return ret;
207 }
208 
209 
223 int32_t mrparam_get_int(mrparam_t* param, int key, int32_t def)
224 {
225  if( param == NULL || key == 0 ) {
226  return def;
227  }
228 
229  char* str = mrparam_get(param, key, NULL);
230  if( str == NULL ) {
231  return def;
232  }
233  int32_t ret = atol(str);
234  free(str);
235  return ret;
236 }
237 
238 
253 void mrparam_set(mrparam_t* param, int key, const char* value)
254 {
255  char *old1, *old2, *new1 = NULL;
256 
257  if( param == NULL || key == 0 ) {
258  return;
259  }
260 
261  old1 = param->m_packed;
262  old2 = NULL;
263 
264  /* remove existing parameter from packed string, if any */
265  if( old1 ) {
266  char *p1, *p2;
267  p1 = find_param(old1, key, &p2);
268  if( p1 != NULL ) {
269  *p1 = 0;
270  old2 = p2;
271  }
272  else if( value==NULL ) {
273  return; /* parameter does not exist and should be cleared -> done. */
274  }
275  }
276 
277  mr_rtrim(old1); /* trim functions are null-pointer-safe */
278  mr_ltrim(old2);
279 
280  if( old1 && old1[0]==0 ) { old1 = NULL; }
281  if( old2 && old2[0]==0 ) { old2 = NULL; }
282 
283  /* create new string */
284  if( value ) {
285  new1 = mr_mprintf("%s%s%c=%s%s%s",
286  old1? old1 : "",
287  old1? "\n" : "",
288  key,
289  value,
290  old2? "\n" : "",
291  old2? old2 : "");
292  }
293  else {
294  new1 = mr_mprintf("%s%s%s",
295  old1? old1 : "",
296  (old1&&old2)? "\n" : "",
297  old2? old2 : "");
298  }
299 
300  free(param->m_packed);
301  param->m_packed = new1;
302 }
303 
304 
318 void mrparam_set_int(mrparam_t* param, int key, int32_t value)
319 {
320  if( param == NULL || key == 0 ) {
321  return;
322  }
323 
324  char* value_str = mr_mprintf("%i", (int)value);
325  if( value_str == NULL ) {
326  return;
327  }
328  mrparam_set(param, key, value_str);
329  free(value_str);
330 }
int mrparam_exists(mrparam_t *param, int key)
Check if a parameter exists.
Definition: mrparam.c:161
-
void mrparam_unref(mrparam_t *param)
Free an parameter list object created eg.
Definition: mrparam.c:90
-
char * mrparam_get(mrparam_t *param, int key, const char *def)
Get value of a parameter.
Definition: mrparam.c:186
-
void mrparam_set(mrparam_t *param, int key, const char *value)
Set parameter to a string.
Definition: mrparam.c:253
-
mrparam_t * mrparam_new()
Create new parameter list object.
Definition: mrparam.c:69
-
void mrparam_set_int(mrparam_t *param, int key, int32_t value)
Set parameter to an integer.
Definition: mrparam.c:318
-
int32_t mrparam_get_int(mrparam_t *param, int key, int32_t def)
Get value of a parameter.
Definition: mrparam.c:223
-
An object for handling key=value parameter lists.
Definition: mrparam.h:36
-
void mrparam_empty(mrparam_t *param)
Delete all parameters.
Definition: mrparam.c:111
-
- - - - diff --git a/docs/user/html/mrpgp_8c_source.html b/docs/user/html/mrpgp_8c_source.html deleted file mode 100644 index df20b68e..00000000 --- a/docs/user/html/mrpgp_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrpgp.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrpgp.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* End-to-end-encryption and other cryptographic functions based upon OpenSSL
24 and BSD's netpgp.
25 
26 If we want to switch to other encryption engines, here are the functions to
27 be replaced.
28 
29 However, eg. GpgME cannot (easily) be used standalone and GnuPG's licence
30 would not allow the original creator of Delta Chat to release a proprietary
31 version, which, however, is required for the Apple store. (NB: the original
32 creator is the only person who could do this, a normal licensee is not
33 allowed to do so at all)
34 
35 So, we do not see a simple alternative - but everyone is welcome to implement
36 one :-) */
37 
38 
39 #include <openssl/ssl.h>
40 #include <openssl/rand.h>
41 #include <openssl/rsa.h>
42 #include <openssl/evp.h>
43 #include <netpgp-extra.h>
44 #include "mrmailbox_internal.h"
45 #include "mrkey.h"
46 #include "mrkeyring.h"
47 #include "mrpgp.h"
48 
49 
50 static pgp_io_t s_io;
51 
52 
53 void mrpgp_init(mrmailbox_t* mailbox)
54 {
55  #ifdef __APPLE__
56  OPENSSL_init();
57  #else
58  SSL_library_init(); /* older, but more compatible function, simply defined as OPENSSL_init_ssl().
59  SSL_library_init() should be called from the main thread before OpenSSL is called from other threads.
60  libEtPan may call SSL_library_init() again later, however, this should be no problem.
61  SSL_library_init() always returns "1", so it is safe to discard the return value */
62  #endif
63 
64  /* setup i/o structure */
65  memset(&s_io, 0, sizeof(pgp_io_t));
66  s_io.outs = stdout;
67  s_io.errs = stderr;
68  s_io.res = stderr;
69 }
70 
71 
72 void mrpgp_exit(mrmailbox_t* mailbox)
73 {
74 }
75 
76 
77 void mrpgp_rand_seed(mrmailbox_t* mailbox, const void* buf, size_t bytes)
78 {
79  if( buf == NULL || bytes <= 0 ) {
80  return;
81  }
82 
83  RAND_seed(buf, bytes);
84 }
85 
86 
87 /*******************************************************************************
88  * Key generatation
89  ******************************************************************************/
90 
91 
92 static unsigned add_key_prefs(pgp_create_sig_t *sig)
93 {
94  /* similar to pgp_add_key_prefs(), Mimic of GPG default settings, limited to supported algos */
95  return
96  /* Symmetric algo prefs */
97  pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_SKA) &&
98  pgp_write_scalar(sig->output, PGP_SA_AES_256, 1) &&
99  pgp_write_scalar(sig->output, PGP_SA_AES_128, 1) &&
100  pgp_write_scalar(sig->output, PGP_SA_CAST5, 1) &&
101  pgp_write_scalar(sig->output, PGP_SA_TRIPLEDES, 1) &&
102  pgp_write_scalar(sig->output, PGP_SA_IDEA, 1) &&
103 
104  /* Hash algo prefs, the first algo is the preferred algo */
105  pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_HASH) &&
106  pgp_write_scalar(sig->output, PGP_HASH_SHA256, 1) &&
107  pgp_write_scalar(sig->output, PGP_HASH_SHA384, 1) &&
108  pgp_write_scalar(sig->output, PGP_HASH_SHA512, 1) &&
109  pgp_write_scalar(sig->output, PGP_HASH_SHA224, 1) &&
110  pgp_write_scalar(sig->output, PGP_HASH_SHA1, 1) && /* Edit for Autocrypt/Delta Chat: due to the weak SHA1, it should not be preferred */
111 
112  /* Compression algo prefs */
113  pgp_write_ss_header(sig->output, 2/*1+number of following items*/, PGP_PTAG_SS_PREF_COMPRESS) &&
114  pgp_write_scalar(sig->output, PGP_C_ZLIB, 1) /*&& -- not sure if Delta Chat will support bzip2 on all platforms, however, this is not that important as typical files are compressed themselves and text is not that big
115  pgp_write_scalar(sig->output, PGP_C_BZIP2, 1) -- if you re-enable this, do not forget to modifiy the header count*/;
116 }
117 
118 
119 static void add_selfsigned_userid(pgp_key_t *skey, pgp_key_t *pkey, const uint8_t *userid, time_t key_expiry)
120 {
121  /* similar to pgp_add_selfsigned_userid() which, however, uses different key flags */
122  pgp_create_sig_t *sig;
123  pgp_subpacket_t sigpacket;
124  pgp_memory_t *mem_sig = NULL;
125  pgp_output_t *sigoutput = NULL;
126 
127  /* create sig for this pkt */
128  sig = pgp_create_sig_new();
129  pgp_sig_start_key_sig(sig, &skey->key.seckey.pubkey, NULL, userid, PGP_CERT_POSITIVE);
130 
131  pgp_add_creation_time(sig, time(NULL));
132  pgp_add_key_expiration_time(sig, key_expiry);
133  pgp_add_primary_userid(sig, 1);
134  pgp_add_key_flags(sig, PGP_KEYFLAG_SIGN_DATA|PGP_KEYFLAG_CERT_KEYS);
135  add_key_prefs(sig);
136  pgp_add_key_features(sig); /* will add 0x01 - modification detection */
137 
138  pgp_end_hashed_subpkts(sig);
139 
140  pgp_add_issuer_keyid(sig, skey->pubkeyid); /* the issuer keyid is not hashed by definition */
141 
142  pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
143  pgp_write_sig(sigoutput, sig, &skey->key.seckey.pubkey, &skey->key.seckey);
144 
145  /* add this packet to key */
146  sigpacket.length = pgp_mem_len(mem_sig);
147  sigpacket.raw = pgp_mem_data(mem_sig);
148 
149  /* add user id and signature to key */
150  pgp_update_userid(skey, userid, &sigpacket, &sig->sig.info);
151  if(pkey) {
152  pgp_update_userid(pkey, userid, &sigpacket, &sig->sig.info);
153  }
154 
155  /* cleanup */
156  pgp_create_sig_delete(sig);
157  pgp_output_delete(sigoutput);
158  pgp_memory_free(mem_sig);
159 }
160 
161 
162 static void add_subkey_binding_signature(pgp_subkeysig_t* p, pgp_key_t* primarykey, pgp_key_t* subkey, pgp_key_t* seckey)
163 {
164  /*add "0x18: Subkey Binding Signature" packet, PGP_SIG_SUBKEY */
165  pgp_create_sig_t* sig;
166  pgp_output_t* sigoutput = NULL;
167  pgp_memory_t* mem_sig = NULL;
168 
169  sig = pgp_create_sig_new();
170  pgp_sig_start_key_sig(sig, &primarykey->key.pubkey, &subkey->key.pubkey, NULL, PGP_SIG_SUBKEY);
171 
172  pgp_add_creation_time(sig, time(NULL));
173  pgp_add_key_expiration_time(sig, 0);
174  pgp_add_key_flags(sig, PGP_KEYFLAG_ENC_STORAGE|PGP_KEYFLAG_ENC_COMM); /* NB: algo/hash/compression preferences are not added to subkeys */
175 
176  pgp_end_hashed_subpkts(sig);
177 
178  pgp_add_issuer_keyid(sig, seckey->pubkeyid); /* the issuer keyid is not hashed by definition */
179 
180  pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
181  pgp_write_sig(sigoutput, sig, &seckey->key.seckey.pubkey, &seckey->key.seckey);
182 
183  p->subkey = primarykey->subkeyc-1; /* index of subkey in array */
184  p->packet.length = mem_sig->length;
185  p->packet.raw = mem_sig->buf; mem_sig->buf = NULL; /* move ownership to packet */
186  copy_sig_info(&p->siginfo, &sig->sig.info); /* not sure, if this is okay, however, siginfo should be set up, otherwise we get "bad info-type" errors */
187 
188  pgp_create_sig_delete(sig);
189  pgp_output_delete(sigoutput);
190  free(mem_sig); /* do not use pgp_memory_free() as this would also free mem_sig->buf which is owned by the packet */
191 }
192 
193 
194 int mrpgp_create_keypair(mrmailbox_t* mailbox, const char* addr, mrkey_t* ret_public_key, mrkey_t* ret_private_key)
195 {
196  int success = 0;
197  pgp_key_t seckey, pubkey, subkey;
198  uint8_t subkeyid[PGP_KEY_ID_SIZE];
199  uint8_t* user_id = NULL;
200  pgp_memory_t *pubmem = pgp_memory_new(), *secmem = pgp_memory_new();
201  pgp_output_t *pubout = pgp_output_new(), *secout = pgp_output_new();
202 
203  memset(&seckey, 0, sizeof(pgp_key_t));
204  memset(&pubkey, 0, sizeof(pgp_key_t));
205  memset(&subkey, 0, sizeof(pgp_key_t));
206 
207  if( mailbox==NULL || addr==NULL || ret_public_key==NULL || ret_private_key==NULL
208  || pubmem==NULL || secmem==NULL || pubout==NULL || secout==NULL ) {
209  goto cleanup;
210  }
211 
212  /* Generate User ID. For convention, use the same address as given in `Autocrypt: to=...` in angle brackets
213  (RFC 2822 grammar angle-addr, see also https://autocrypt.org/en/latest/level0.html#type-p-openpgp-based-key-data )
214  We do not add the name to the ID for the following reasons:
215  - privacy
216  - the name may be changed
217  - shorter keys
218  - the name is already taken from From:
219  - not Autocrypt:-standard */
220  user_id = (uint8_t*)mr_mprintf("<%s>", addr);
221 
222  /* generate two keypairs */
223  if( !pgp_rsa_generate_keypair(&seckey, 3072/*bits*/, 65537UL/*e*/, NULL, NULL, NULL, 0)
224  || !pgp_rsa_generate_keypair(&subkey, 3072/*bits*/, 65537UL/*e*/, NULL, NULL, NULL, 0) ) {
225  goto cleanup;
226  }
227 
228 
229  /* Create public key, bind public subkey to public key
230  ------------------------------------------------------------------------ */
231 
232  pubkey.type = PGP_PTAG_CT_PUBLIC_KEY;
233  pgp_pubkey_dup(&pubkey.key.pubkey, &seckey.key.pubkey);
234  memcpy(pubkey.pubkeyid, seckey.pubkeyid, PGP_KEY_ID_SIZE);
235  pgp_fingerprint(&pubkey.pubkeyfpr, &seckey.key.pubkey, 0);
236  add_selfsigned_userid(&seckey, &pubkey, (const uint8_t*)user_id, 0/*never expire*/);
237 
238  EXPAND_ARRAY((&pubkey), subkey);
239  {
240  pgp_subkey_t* p = &pubkey.subkeys[pubkey.subkeyc++];
241  pgp_pubkey_dup(&p->key.pubkey, &subkey.key.pubkey);
242  pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &pubkey.key.pubkey, PGP_HASH_SHA1);
243  memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE);
244  }
245 
246  EXPAND_ARRAY((&pubkey), subkeysig);
247  add_subkey_binding_signature(&pubkey.subkeysigs[pubkey.subkeysigc++], &pubkey, &subkey, &seckey);
248 
249 
250  /* Create secret key, bind secret subkey to secret key
251  ------------------------------------------------------------------------ */
252 
253  EXPAND_ARRAY((&seckey), subkey);
254  {
255  pgp_subkey_t* p = &seckey.subkeys[seckey.subkeyc++];
256  pgp_seckey_dup(&p->key.seckey, &subkey.key.seckey);
257  pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &seckey.key.pubkey, PGP_HASH_SHA1);
258  memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE);
259  }
260 
261  EXPAND_ARRAY((&seckey), subkeysig);
262  add_subkey_binding_signature(&seckey.subkeysigs[seckey.subkeysigc++], &seckey, &subkey, &seckey);
263 
264 
265  /* Done with key generation, write binary keys to memory
266  ------------------------------------------------------------------------ */
267 
268  pgp_writer_set_memory(pubout, pubmem);
269  if( !pgp_write_xfer_key(pubout, &pubkey, 0/*armored*/)
270  || pubmem->buf == NULL || pubmem->length <= 0 ) {
271  goto cleanup;
272  }
273 
274  pgp_writer_set_memory(secout, secmem);
275  if( !pgp_write_xfer_key(secout, &seckey, 0/*armored*/)
276  || secmem->buf == NULL || secmem->length <= 0 ) {
277  goto cleanup;
278  }
279 
280  mrkey_set_from_raw(ret_public_key, pubmem->buf, pubmem->length, MR_PUBLIC);
281  mrkey_set_from_raw(ret_private_key, secmem->buf, secmem->length, MR_PRIVATE);
282 
283  success = 1;
284 
285 cleanup:
286  if( pubout ) { pgp_output_delete(pubout); }
287  if( secout ) { pgp_output_delete(secout); }
288  if( pubmem ) { pgp_memory_free(pubmem); }
289  if( secmem ) { pgp_memory_free(secmem); }
290  pgp_key_free(&seckey); /* not: pgp_keydata_free() which will also free the pointer itself (we created it on the stack) */
291  pgp_key_free(&pubkey);
292  pgp_key_free(&subkey);
293  free(user_id);
294  return success;
295 }
296 
297 
298 /*******************************************************************************
299  * Check keys
300  ******************************************************************************/
301 
302 
303 int mrpgp_is_valid_key(mrmailbox_t* mailbox, const mrkey_t* raw_key)
304 {
305  int key_is_valid = 0;
306  pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t));
307  pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t));
308  pgp_memory_t* keysmem = pgp_memory_new();
309 
310  if( mailbox==NULL || raw_key==NULL
311  || raw_key->m_binary == NULL || raw_key->m_bytes <= 0
312  || public_keys==NULL || private_keys==NULL || keysmem==NULL ) {
313  goto cleanup;
314  }
315 
316  pgp_memory_add(keysmem, raw_key->m_binary, raw_key->m_bytes);
317 
318  pgp_filter_keys_from_mem(&s_io, public_keys, private_keys, NULL, 0, keysmem); /* function returns 0 on any error in any packet - this does not mean, we cannot use the key. We check the details below therefore. */
319 
320  if( raw_key->m_type == MR_PUBLIC && public_keys->keyc >= 1 ) {
321  key_is_valid = 1;
322  }
323  else if( raw_key->m_type == MR_PRIVATE && private_keys->keyc >= 1 ) {
324  key_is_valid = 1;
325  }
326 
327 cleanup:
328  if( keysmem ) { pgp_memory_free(keysmem); }
329  if( public_keys ) { pgp_keyring_purge(public_keys); free(public_keys); } /*pgp_keyring_free() frees the content, not the pointer itself*/
330  if( private_keys ) { pgp_keyring_purge(private_keys); free(private_keys); }
331  return key_is_valid;
332 }
333 
334 
335 int mrpgp_calc_fingerprint(mrmailbox_t* mailbox, const mrkey_t* raw_key, uint8_t** ret_fingerprint, size_t* ret_fingerprint_bytes)
336 {
337  int success = 0;
338  pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t));
339  pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t));
340  pgp_memory_t* keysmem = pgp_memory_new();
341 
342  if( mailbox==NULL || raw_key==NULL || ret_fingerprint==NULL || *ret_fingerprint!=NULL || ret_fingerprint_bytes==NULL || *ret_fingerprint_bytes!=0
343  || raw_key->m_binary == NULL || raw_key->m_bytes <= 0
344  || public_keys==NULL || private_keys==NULL || keysmem==NULL ) {
345  goto cleanup;
346  }
347 
348  pgp_memory_add(keysmem, raw_key->m_binary, raw_key->m_bytes);
349 
350  pgp_filter_keys_from_mem(&s_io, public_keys, private_keys, NULL, 0, keysmem);
351 
352  if( raw_key->m_type != MR_PUBLIC || public_keys->keyc <= 0 ) {
353  goto cleanup;
354  }
355 
356  pgp_key_t* key0 = &public_keys->keys[0];
357  pgp_pubkey_t* pubkey0 = &key0->key.pubkey;
358  if( !pgp_fingerprint(&key0->pubkeyfpr, pubkey0, 0) ) {
359  goto cleanup;
360  }
361 
362  *ret_fingerprint_bytes = key0->pubkeyfpr.length;
363  *ret_fingerprint = malloc(*ret_fingerprint_bytes);
364  memcpy(*ret_fingerprint, key0->pubkeyfpr.fingerprint, *ret_fingerprint_bytes);
365 
366  success = 1;
367 
368 cleanup:
369  if( keysmem ) { pgp_memory_free(keysmem); }
370  if( public_keys ) { pgp_keyring_purge(public_keys); free(public_keys); } /*pgp_keyring_free() frees the content, not the pointer itself*/
371  if( private_keys ) { pgp_keyring_purge(private_keys); free(private_keys); }
372  return success;
373 }
374 
375 
376 int mrpgp_split_key(mrmailbox_t* mailbox, const mrkey_t* private_in, mrkey_t* ret_public_key)
377 {
378  int success = 0;
379  pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t));
380  pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t));
381  pgp_memory_t* keysmem = pgp_memory_new();
382  pgp_memory_t* pubmem = pgp_memory_new();
383  pgp_output_t* pubout = pgp_output_new();
384 
385  if( mailbox == NULL || private_in==NULL || ret_public_key==NULL
386  || public_keys==NULL || private_keys==NULL || keysmem==NULL || pubmem==NULL || pubout==NULL ) {
387  goto cleanup;
388  }
389 
390  pgp_memory_add(keysmem, private_in->m_binary, private_in->m_bytes);
391  pgp_filter_keys_from_mem(&s_io, public_keys, private_keys, NULL, 0, keysmem);
392 
393  if( private_in->m_type!=MR_PRIVATE || private_keys->keyc <= 0 ) {
394  mrmailbox_log_warning(mailbox, 0, "Split key: Given key is no private key.");
395  goto cleanup;
396  }
397 
398  if( public_keys->keyc <= 0 ) {
399  mrmailbox_log_warning(mailbox, 0, "Split key: Given key does not contain a public key.");
400  goto cleanup;
401  }
402 
403  pgp_writer_set_memory(pubout, pubmem);
404  if( !pgp_write_xfer_key(pubout, &public_keys->keys[0], 0/*armored*/)
405  || pubmem->buf == NULL || pubmem->length <= 0 ) {
406  goto cleanup;
407  }
408 
409  mrkey_set_from_raw(ret_public_key, pubmem->buf, pubmem->length, MR_PUBLIC);
410 
411  success = 1;
412 
413 cleanup:
414  if( pubout ) { pgp_output_delete(pubout); }
415  if( pubmem ) { pgp_memory_free(pubmem); }
416  if( keysmem ) { pgp_memory_free(keysmem); }
417  if( public_keys ) { pgp_keyring_purge(public_keys); free(public_keys); } /*pgp_keyring_free() frees the content, not the pointer itself*/
418  if( private_keys ) { pgp_keyring_purge(private_keys); free(private_keys); }
419  return success;
420 }
421 
422 
423 /*******************************************************************************
424  * Public key encrypt/decrypt
425  ******************************************************************************/
426 
427 
428 int mrpgp_pk_encrypt( mrmailbox_t* mailbox,
429  const void* plain_text,
430  size_t plain_bytes,
431  const mrkeyring_t* raw_public_keys_for_encryption,
432  const mrkey_t* raw_private_key_for_signing,
433  int use_armor,
434  void** ret_ctext,
435  size_t* ret_ctext_bytes)
436 {
437  pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t));
438  pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t));
439  pgp_keyring_t* dummy_keys = calloc(1, sizeof(pgp_keyring_t));
440  pgp_memory_t* keysmem = pgp_memory_new();
441  pgp_memory_t* signedmem = NULL;
442  int i, success = 0;
443 
444  if( mailbox==NULL || plain_text==NULL || plain_bytes==0 || ret_ctext==NULL || ret_ctext_bytes==NULL
445  || raw_public_keys_for_encryption==NULL || raw_public_keys_for_encryption->m_count<=0
446  || keysmem==NULL || public_keys==NULL || private_keys==NULL || dummy_keys==NULL ) {
447  goto cleanup;
448  }
449 
450  *ret_ctext = NULL;
451  *ret_ctext_bytes = 0;
452 
453  /* setup keys (the keys may come from pgp_filter_keys_fileread(), see also pgp_keyring_add(rcpts, key)) */
454  for( i = 0; i < raw_public_keys_for_encryption->m_count; i++ ) {
455  pgp_memory_add(keysmem, raw_public_keys_for_encryption->m_keys[i]->m_binary, raw_public_keys_for_encryption->m_keys[i]->m_bytes);
456  }
457 
458  pgp_filter_keys_from_mem(&s_io, public_keys, private_keys/*should stay empty*/, NULL, 0, keysmem);
459  if( public_keys->keyc <=0 || private_keys->keyc!=0 ) {
460  mrmailbox_log_warning(mailbox, 0, "Encryption-keyring contains unexpected data (%i/%i)", public_keys->keyc, private_keys->keyc);
461  goto cleanup;
462  }
463 
464  /* encrypt */
465  {
466  const void* signed_text = NULL;
467  size_t signed_bytes = 0;
468  int encrypt_raw_packet = 0;
469 
470  if( raw_private_key_for_signing ) {
471  pgp_memory_clear(keysmem);
472  pgp_memory_add(keysmem, raw_private_key_for_signing->m_binary, raw_private_key_for_signing->m_bytes);
473  pgp_filter_keys_from_mem(&s_io, dummy_keys, private_keys, NULL, 0, keysmem);
474  if( private_keys->keyc <= 0 ) {
475  mrmailbox_log_warning(mailbox, 0, "No key for signing found.");
476  goto cleanup;
477  }
478 
479  pgp_key_t* sk0 = &private_keys->keys[0];
480  signedmem = pgp_sign_buf(&s_io, plain_text, plain_bytes, &sk0->key.seckey, time(NULL)/*birthtime*/, 0/*duration*/, "sha1", 0/*armored*/, 0/*cleartext*/);
481  if( signedmem == NULL ) {
482  mrmailbox_log_warning(mailbox, 0, "Signing failed.");
483  goto cleanup;
484  }
485  signed_text = signedmem->buf;
486  signed_bytes = signedmem->length;
487  encrypt_raw_packet = 1;
488  }
489  else {
490  signed_text = plain_text;
491  signed_bytes = plain_bytes;
492  encrypt_raw_packet = 0;
493  }
494 
495  pgp_memory_t* outmem = pgp_encrypt_buf(&s_io, signed_text, signed_bytes, public_keys, use_armor, NULL/*cipher*/, encrypt_raw_packet);
496  if( outmem == NULL ) {
497  mrmailbox_log_warning(mailbox, 0, "Encryption failed.");
498  goto cleanup;
499  }
500  *ret_ctext = outmem->buf;
501  *ret_ctext_bytes = outmem->length;
502  free(outmem); /* do not use pgp_memory_free() as we took ownership of the buffer */
503  }
504 
505  success = 1;
506 
507 cleanup:
508  if( keysmem ) { pgp_memory_free(keysmem); }
509  if( signedmem ) { pgp_memory_free(signedmem); }
510  if( public_keys ) { pgp_keyring_purge(public_keys); free(public_keys); } /*pgp_keyring_free() frees the content, not the pointer itself*/
511  if( private_keys ) { pgp_keyring_purge(private_keys); free(private_keys); }
512  if( dummy_keys ) { pgp_keyring_purge(dummy_keys); free(dummy_keys); }
513  return success;
514 }
515 
516 
517 int mrpgp_pk_decrypt( mrmailbox_t* mailbox,
518  const void* ctext,
519  size_t ctext_bytes,
520  const mrkeyring_t* raw_private_keys_for_decryption,
521  const mrkey_t* raw_public_key_for_validation,
522  int use_armor,
523  void** ret_plain,
524  size_t* ret_plain_bytes,
525  int* ret_validation_errors)
526 {
527  pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t)); /*should be 0 after parsing*/
528  pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t));
529  pgp_keyring_t* dummy_keys = calloc(1, sizeof(pgp_keyring_t));
530  pgp_validation_t* vresult = calloc(1, sizeof(pgp_validation_t));
531  key_id_t* recipients_key_ids = NULL;
532  unsigned recipients_count = 0;
533  pgp_memory_t* keysmem = pgp_memory_new();
534  int i, success = 0;
535 
536  if( mailbox==NULL || ctext==NULL || ctext_bytes==0 || ret_plain==NULL || ret_plain_bytes==NULL || ret_validation_errors==NULL
537  || raw_private_keys_for_decryption==NULL || raw_private_keys_for_decryption->m_count<=0
538  || vresult==NULL || keysmem==NULL || public_keys==NULL || private_keys==NULL ) {
539  goto cleanup;
540  }
541 
542  *ret_plain = NULL;
543  *ret_plain_bytes = 0;
544 
545  /* setup keys (the keys may come from pgp_filter_keys_fileread(), see also pgp_keyring_add(rcpts, key)) */
546  for( i = 0; i < raw_private_keys_for_decryption->m_count; i++ ) {
547  pgp_memory_add(keysmem, raw_private_keys_for_decryption->m_keys[i]->m_binary, raw_private_keys_for_decryption->m_keys[i]->m_bytes);
548  }
549 
550  pgp_filter_keys_from_mem(&s_io, dummy_keys/*should stay empty*/, private_keys, NULL, 0, keysmem);
551  if( private_keys->keyc<=0 ) {
552  mrmailbox_log_warning(mailbox, 0, "Decryption-keyring contains unexpected data (%i/%i)", public_keys->keyc, private_keys->keyc);
553  goto cleanup;
554  }
555 
556  if( raw_public_key_for_validation ) {
557  pgp_memory_clear(keysmem);
558  pgp_memory_add(keysmem, raw_public_key_for_validation->m_binary, raw_public_key_for_validation->m_bytes);
559  pgp_filter_keys_from_mem(&s_io, public_keys, dummy_keys/*should stay empty*/, NULL, 0, keysmem);
560  }
561 
562  /* decrypt */
563  {
564  pgp_memory_t* outmem = pgp_decrypt_and_validate_buf(&s_io, vresult, ctext, ctext_bytes, private_keys, public_keys,
565  use_armor, &recipients_key_ids, &recipients_count);
566  if( outmem == NULL ) {
567  mrmailbox_log_warning(mailbox, 0, "Decryption failed.");
568  goto cleanup;
569  }
570  *ret_plain = outmem->buf;
571  *ret_plain_bytes = outmem->length;
572  free(outmem); /* do not use pgp_memory_free() as we took ownership of the buffer */
573 
574  /* validate */
575  *ret_validation_errors = 0;
576  if( vresult->validc <= 0 && vresult->invalidc <= 0 && vresult->unknownc <= 0 )
577  {
578  /* no valid nor invalid signatures found */
579  *ret_validation_errors = MR_VALIDATE_NO_SIGNATURE;
580  }
581  else if( raw_public_key_for_validation==NULL || vresult->unknownc > 0 )
582  {
583  /* at least one valid or invalid signature found, but no key for verification */
584  *ret_validation_errors = MR_VALIDATE_UNKNOWN_SIGNATURE;
585  }
586  else if( vresult->invalidc > 0 )
587  {
588  /* at least one invalid signature found */
589  *ret_validation_errors = MR_VALIDATE_BAD_SIGNATURE;
590  }
591  else
592  {
593  /* only valid signatures found */
594  ;
595  }
596  }
597 
598  success = 1;
599 
600 cleanup:
601  if( keysmem ) { pgp_memory_free(keysmem); }
602  if( public_keys ) { pgp_keyring_purge(public_keys); free(public_keys); } /*pgp_keyring_free() frees the content, not the pointer itself*/
603  if( private_keys ) { pgp_keyring_purge(private_keys); free(private_keys); }
604  if( dummy_keys ) { pgp_keyring_purge(dummy_keys); free(dummy_keys); }
605  if( vresult ) { pgp_validate_result_free(vresult); }
606  if( recipients_key_ids ) { free(recipients_key_ids); }
607  return success;
608 }
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrpoortext_8c_source.html b/docs/user/html/mrpoortext_8c_source.html deleted file mode 100644 index 826e269a..00000000 --- a/docs/user/html/mrpoortext_8c_source.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrpoortext.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrpoortext.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 
25 
26 /*******************************************************************************
27  * Main interface
28  ******************************************************************************/
29 
30 
31 mrpoortext_t* mrpoortext_new()
32 {
33  mrpoortext_t* ths = NULL;
34 
35  if( (ths=calloc(1, sizeof(mrpoortext_t)))==NULL ) {
36  exit(27); /* cannot allocate little memory, unrecoverable error */
37  }
38 
40 
41  return ths;
42 }
43 
44 
56 {
57  if( poortext==NULL ) {
58  return;
59  }
60 
61  mrpoortext_empty(poortext);
62  free(poortext);
63 }
64 
65 
66 void mrpoortext_empty(mrpoortext_t* ths)
67 {
68  if( ths == NULL ) {
69  return;
70  }
71 
72  free(ths->m_text1);
73  ths->m_text1 = NULL;
75 
76  free(ths->m_text2);
77  ths->m_text2 = NULL;
78 
79  ths->m_timestamp = 0;
80  ths->m_state = 0;
81 }
82 
83 
84 void mrpoortext_fill(mrpoortext_t* ths, const mrmsg_t* msg, const mrchat_t* chat, const mrcontact_t* contact)
85 {
86  if( ths == NULL || msg == NULL ) {
87  return;
88  }
89 
90  if( msg->m_from_id == MR_CONTACT_ID_SELF )
91  {
92  ths->m_text1 = mrstock_str(MR_STR_SELF);
94  }
95  else if( chat == NULL )
96  {
97  free(ths->m_text1);
98  ths->m_text1 = NULL;
100  }
101  else if( chat->m_type==MR_CHAT_TYPE_GROUP )
102  {
103  if( contact==NULL ) {
104  free(ths->m_text1);
105  ths->m_text1 = NULL;
107  }
108  else if( contact->m_name && contact->m_name[0] ) {
109  ths->m_text1 = mrcontact_get_first_name(contact->m_name);
111  }
112  else if( contact->m_addr && contact->m_addr[0] ) {
113  ths->m_text1 = safe_strdup(contact->m_addr);
115  }
116  else {
117  ths->m_text1 = safe_strdup("Unnamed contact");
119  }
120  }
121 
122  ths->m_text2 = mrmsg_get_summarytext_by_raw(msg->m_type, msg->m_text, msg->m_param, MR_SUMMARY_CHARACTERS);
123  ths->m_timestamp = msg->m_timestamp;
124  ths->m_state = msg->m_state;
125 }
void mrpoortext_unref(mrpoortext_t *poortext)
Frees a mrpoortext_t object created eg.
Definition: mrpoortext.c:55
-
char * mrcontact_get_first_name(const char *full_name)
Get the first name.
Definition: mrcontact.c:99
-
An object representing a single contact in memory.
Definition: mrcontact.h:38
-
#define MR_TEXT1_USERNAME
Definition: mrpoortext.h:47
-
char * m_text2
may be NULL
Definition: mrpoortext.h:39
-
int m_type
Message type as one of the MR_MSG_* contstants.
Definition: mrmsg.h:60
-
int m_state
may be 0
Definition: mrpoortext.h:41
-
time_t m_timestamp
may be 0
Definition: mrpoortext.h:40
-
char * m_text1
may be NULL
Definition: mrpoortext.h:38
-
the poortext object and some function accessing it.
Definition: mrpoortext.h:35
-
#define MR_TEXT1_NORMAL
Definition: mrpoortext.h:45
-
An object representing a single message in memory.
Definition: mrmsg.h:40
-
int m_text1_meaning
One of MR_TEXT1_NORMAL, MR_TEXT1_DRAFT, MR_TEXT1_USERNAME or MR_TEXT1_SELF.
Definition: mrpoortext.h:37
-
uint32_t m_from_id
Contact ID of the sender.
Definition: mrmsg.h:47
-
#define MR_TEXT1_SELF
Definition: mrpoortext.h:48
-
mrparam_t * m_param
MRP_FILE, MRP_WIDTH, MRP_HEIGHT etc.
Definition: mrmsg.h:73
-
char * m_text
message text or NULL if unset
Definition: mrmsg.h:72
-
int m_state
Message state as one of the MR_MSG_STATE_* contstants.
Definition: mrmsg.h:70
-
char * m_addr
may be NULL or empty
Definition: mrcontact.h:47
-
char * m_name
may be NULL or empty, this name should not be spreaded as it may be "Daddy" and so on; initially set ...
Definition: mrcontact.h:45
-
time_t m_timestamp
Unix time the message was sended or received.
Definition: mrmsg.h:50
-
An object representing a single chat in memory.
Definition: mrchat.h:39
-
- - - - diff --git a/docs/user/html/mrsaxparser_8c_source.html b/docs/user/html/mrsaxparser_8c_source.html deleted file mode 100644 index 887a223f..00000000 --- a/docs/user/html/mrsaxparser_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrsaxparser.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrsaxparser.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* mrsaxparser_t parses XML and HTML files that may not be wellformed
24 and spits out all text and tags found.
25 - Attributes are recognized with single, double or no quotes
26 - Whitespace ignored inside tags
27 - Self-closing tags are issued as open-tag plus close-tag
28 - CDATA is supoorted; DTA, comments, processing instruction are
29  skipped properly
30 - The parser does not care about hierarchy, if needed this can be
31  done by the user.
32 - Input and output strings must be UTF-8 encoded.
33 - Tag and attribute names are converted to lower case.
34 - Parsing does not stop on errors; instead errors are recovered. */
35 
36 
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include "mrmailbox.h"
41 #include "mrtools.h"
42 #include "mrsaxparser.h"
43 
44 
45 /*******************************************************************************
46  * Decoding text
47  ******************************************************************************/
48 
49 
50 static const char* s_ent[] = {
51  /* Convert entities as &auml; to UTF-8 characters.
52 
53  - The first strings MUST NOT start with `&` and MUST end with `;`.
54  - take care not to miss a comma between the strings.
55  - It's also possible to specify the destination as a character reference as `&#34;` (they are converted in a second pass without a table). */
56 
57  /* basic XML/HTML */
58  "lt;", "<", "gt;", ">", "quot;", "\"", "apos;", "'",
59  "amp;", "&", "nbsp;", " ",
60 
61  /* advanced HTML */
62  "iexcl;", "¡", "cent;", "¢", "pound;", "£", "curren;", "¤",
63  "yen;", "¥", "brvbar;", "¦", "sect;", "§", "uml;", "¨",
64  "copy;", "©", "ordf;", "ª", "laquo;", "«", "not;", "¬",
65  "shy;", "-", "reg;", "®", "macr;", "¯", "deg;", "°",
66  "plusmn;", "±", "sup2;", "²", "sup3;", "³", "acute;", "´",
67  "micro;", "µ", "para;", "¶", "middot;", "·", "cedil;", "¸",
68  "sup1;", "¹", "ordm;", "º", "raquo;", "»", "frac14;", "¼",
69  "frac12;", "½", "frac34;", "¾", "iquest;", "¿", "Agrave;", "À",
70  "Aacute;", "Á", "Acirc;", "Â", "Atilde;", "Ã", "Auml;", "Ä",
71  "Aring;", "Å", "AElig;", "Æ", "Ccedil;", "Ç", "Egrave;", "È",
72  "Eacute;", "É", "Ecirc;", "Ê", "Euml;", "Ë", "Igrave;", "Ì",
73  "Iacute;", "Í", "Icirc;", "Î", "Iuml;", "Ï", "ETH;", "Ð",
74  "Ntilde;", "Ñ", "Ograve;", "Ò", "Oacute;", "Ó", "Ocirc;", "Ô",
75  "Otilde;", "Õ", "Ouml;", "Ö", "times;", "×", "Oslash;", "Ø",
76  "Ugrave;", "Ù", "Uacute;", "Ú", "Ucirc;", "Û", "Uuml;", "Ü",
77  "Yacute;", "Ý", "THORN;", "Þ", "szlig;", "ß", "agrave;", "à",
78  "aacute;", "á", "acirc;", "â", "atilde;", "ã", "auml;", "ä",
79  "aring;", "å", "aelig;", "æ", "ccedil;", "ç", "egrave;", "è",
80  "eacute;", "é", "ecirc;", "ê", "euml;", "ë", "igrave;", "ì",
81  "iacute;", "í", "icirc;", "î", "iuml;", "ï", "eth;", "ð",
82  "ntilde;", "ñ", "ograve;", "ò", "oacute;", "ó", "ocirc;", "ô",
83  "otilde;", "õ", "ouml;", "ö", "divide;", "÷", "oslash;", "ø",
84  "ugrave;", "ù", "uacute;", "ú", "ucirc;", "û", "uuml;", "ü",
85  "yacute;", "ý", "thorn;", "þ", "yuml;", "ÿ", "OElig;", "Œ",
86  "oelig;", "œ", "Scaron;", "Š", "scaron;", "š", "Yuml;", "Ÿ",
87  "fnof;", "ƒ", "circ;", "ˆ", "tilde;", "˜", "Alpha;", "Α",
88  "Beta;", "Β", "Gamma;", "Γ", "Delta;", "Δ", "Epsilon;", "Ε",
89  "Zeta;", "Ζ", "Eta;", "Η", "Theta;", "Θ", "Iota;", "Ι",
90  "Kappa;", "Κ", "Lambda;", "Λ", "Mu;", "Μ", "Nu;", "Ν",
91  "Xi;", "Ξ", "Omicron;", "Ο", "Pi;", "Π", "Rho;", "Ρ",
92  "Sigma;", "Σ", "Tau;", "Τ", "Upsilon;", "Υ", "Phi;", "Φ",
93  "Chi;", "Χ", "Psi;", "Ψ", "Omega;", "Ω", "alpha;", "α",
94  "beta;", "β", "gamma;", "γ", "delta;", "δ", "epsilon;", "ε",
95  "zeta;", "ζ", "eta;", "η", "theta;", "θ", "iota;", "ι",
96  "kappa;", "κ", "lambda;", "λ", "mu;", "μ", "nu;", "ν",
97  "xi;", "ξ", "omicron;", "ο", "pi;", "π", "rho;", "ρ",
98  "sigmaf;", "ς", "sigma;", "σ", "tau;", "τ", "upsilon;", "υ",
99  "phi;", "φ", "chi;", "χ", "psi;", "ψ", "omega;", "ω",
100  "thetasym;","ϑ", "upsih;", "ϒ", "piv;", "ϖ", "ensp;", " ",
101  "emsp;", " ", "thinsp;", " ", "zwnj;", "" , "zwj;", "" ,
102  "lrm;", "" , "rlm;", "" , "ndash;", "–", "mdash;", "—",
103  "lsquo;", "‘", "rsquo;", "’", "sbquo;", "‚", "ldquo;", "“",
104  "rdquo;", "”", "bdquo;", "„", "dagger;", "†", "Dagger;", "‡",
105  "bull;", "•", "hellip;", "…", "permil;", "‰", "prime;", "′",
106  "Prime;", "″", "lsaquo;", "‹", "rsaquo;", "›", "oline;", "‾",
107  "frasl;", "⁄", "euro;", "€", "image;", "ℑ", "weierp;", "℘",
108  "real;", "ℜ", "trade;", "™", "alefsym;", "ℵ", "larr;", "←",
109  "uarr;", "↑", "rarr;", "→", "darr;", "↓", "harr;", "↔",
110  "crarr;", "↵", "lArr;", "⇐", "uArr;", "⇑", "rArr;", "⇒",
111  "dArr;", "⇓", "hArr;", "⇔", "forall;", "∀", "part;", "∂",
112  "exist;", "∃", "empty;", "∅", "nabla;", "∇", "isin;", "∈",
113  "notin;", "∉", "ni;", "∋", "prod;", "∏", "sum;", "∑",
114  "minus;", "−", "lowast;", "∗", "radic;", "√", "prop;", "∝",
115  "infin;", "∞", "ang;", "∠", "and;", "∧", "or;", "∨",
116  "cap;", "∩", "cup;", "∪", "int;", "∫", "there4;", "∴",
117  "sim;", "∼", "cong;", "≅", "asymp;", "≈", "ne;", "≠",
118  "equiv;", "≡", "le;", "≤", "ge;", "≥", "sub;", "⊂",
119  "sup;", "⊃", "nsub;", "⊄", "sube;", "⊆", "supe;", "⊇",
120  "oplus;", "⊕", "otimes;", "⊗", "perp;", "⊥", "sdot;", "⋅",
121  "lceil;", "⌈", "rceil;", "⌉", "lfloor;", "⌊", "rfloor;", "⌋",
122  "lang;", "<", "rang;", ">", "loz;", "◊", "spades;", "♠",
123  "clubs;", "♣", "hearts;", "♥", "diams;", "♦",
124 
125  /* MUST be last */
126  NULL, NULL,
127 };
128 
129 
130 /* Recursively decodes entity and character references and normalizes new lines.
131 set "type" to ...
132 '&' for general entity decoding,
133 '%' for parameter entity decoding (currently not needed),
134 'c' for cdata sections,
135 ' ' for attribute normalization, or
136 '*' for non-cdata attribute normalization (currently not needed).
137 Returns s, or if the decoded string is longer than s, returns a malloced string
138 that must be freed.
139 Function based upon ezxml_decode() from the "ezxml" parser which is
140 Copyright 2004-2006 Aaron Voisine <aaron@voisine.org> */
141 static char* xml_decode(char* s, char type)
142 {
143  char *e, *r = s, *m = s;
144  long b, c, d, l;
145 
146  for (; *s; s++) { /* normalize line endings */
147  while (*s == '\r') {
148  *(s++) = '\n';
149  if (*s == '\n') memmove(s, (s + 1), strlen(s));
150  }
151  }
152 
153  for (s = r; ; ) {
154  while( *s && *s != '&' /*&& (*s != '%' || type != '%')*/ && !isspace(*s)) s++;
155 
156  if( ! *s )
157  {
158  break;
159  }
160  else if( type != 'c' && ! strncmp(s, "&#", 2) )
161  {
162  /* character reference */
163  if (s[2] == 'x') c = strtol(s + 3, &e, 16); /* base 16 */
164  else c = strtol(s + 2, &e, 10); /* base 10 */
165  if (! c || *e != ';') { s++; continue; } /* not a character ref */
166 
167  if (c < 0x80) *(s++) = c; /* US-ASCII subset */
168  else { /* multi-byte UTF-8 sequence */
169  for (b = 0, d = c; d; d /= 2) b++; /* number of bits in c */
170  b = (b - 2) / 5; /* number of bytes in payload */
171  *(s++) = (0xFF << (7 - b)) | (c >> (6 * b)); /* head */
172  while (b) *(s++) = 0x80 | ((c >> (6 * --b)) & 0x3F); /* payload */
173  }
174 
175  memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';')));
176  }
177  else if( (*s == '&' && (type == '&' || type == ' ' /*|| type == '*'*/))
178  /*|| (*s == '%' && type == '%')*/ )
179  {
180  /* entity reference */
181  for (b = 0; s_ent[b] && strncmp(s + 1, s_ent[b], strlen(s_ent[b])); b += 2)
182  ; /* find entity in entity list */
183 
184  if (s_ent[b++]) { /* found a match */
185  if ((c = strlen(s_ent[b])) - 1 > (e = strchr(s, ';')) - s) {
186  l = (d = (s - r)) + c + strlen(e); /* new length */
187  r = (r == m) ? strcpy(malloc(l), r) : realloc(r, l);
188  e = strchr((s = r + d), ';'); /* fix up pointers */
189  }
190 
191  memmove(s + c, e + 1, strlen(e)); /* shift rest of string */
192  strncpy(s, s_ent[b], c); /* copy in replacement text */
193  }
194  else s++; /* not a known entity */
195  }
196  else if ((type == ' ' /*|| type == '*'*/) && isspace(*s))
197  {
198  *(s++) = ' ';
199  }
200  else s++; /* no decoding needed */
201  }
202 
203  /* normalize spaces for non-cdata attributes
204  if (type == '*') {
205  for (s = r; *s; s++) {
206  if ((l = strspn(s, " "))) memmove(s, s + l, strlen(s + l) + 1);
207  while (*s && *s != ' ') s++;
208  }
209  if (--s >= r && *s == ' ') *s = '\0';
210  }*/
211 
212  return r;
213 }
214 
215 
216 /*******************************************************************************
217  * Tools
218  ******************************************************************************/
219 
220 
221 #define XML_WS "\t\r\n "
222 
223 
224 static void def_starttag_cb (void* userdata, const char* tag, char** attr) { }
225 static void def_endtag_cb (void* userdata, const char* tag) { }
226 static void def_text_cb (void* userdata, const char* text, int len) { }
227 
228 
229 static void call_text_cb(mrsaxparser_t* ths, char* text, size_t len, char type)
230 {
231  if( text && len )
232  {
233  char bak = text[len], *text_new;
234 
235  text[len] = '\0';
236  text_new = xml_decode(text, type);
237  ths->m_text_cb(ths->m_userdata, text_new, len);
238  if( text != text_new ) { free(text_new); }
239 
240  text[len] = bak;
241  }
242 }
243 
244 
245 static void do_free_attr(char** attr, int* free_attr)
246 {
247  /* "attr" are key/value pairs; the function frees the data if the corresponding bit in "free_attr" is set.
248  (we need this as we try to use the strings from the "main" document instead of allocating small strings) */
249  #define FREE_KEY 0x01
250  #define FREE_VALUE 0x02
251  int i = 0;
252  while( attr[i] ) {
253  if( free_attr[i>>1]&FREE_KEY && attr[i] ) { free(attr[i]); }
254  if( free_attr[i>>1]&FREE_VALUE && attr[i+1] ) { free(attr[i+1]); }
255  i += 2;
256  }
257  attr[0] = NULL; /* set list to zero-length */
258 }
259 
260 
261 /*******************************************************************************
262  * Main interface
263  ******************************************************************************/
264 
265 
266 const char* mrattr_find(char** attr, const char* key)
267 {
268  if( attr && key ) {
269  int i = 0;
270  while( attr[i] && strcmp(key, attr[i]) ) {
271  i += 2;
272  }
273 
274  if( attr[i] ) {
275  return attr[i + 1];
276  }
277  }
278  return NULL;
279 }
280 
281 
282 void mrsaxparser_init(mrsaxparser_t* ths, void* userdata)
283 {
284  ths->m_userdata = userdata;
285  ths->m_starttag_cb = def_starttag_cb;
286  ths->m_endtag_cb = def_endtag_cb;
287  ths->m_text_cb = def_text_cb;
288 }
289 
290 
291 void mrsaxparser_set_tag_handler(mrsaxparser_t* ths, mrsaxparser_starttag_cb_t starttag_cb, mrsaxparser_endtag_cb_t endtag_cb)
292 {
293  if( ths == NULL ) {
294  return;
295  }
296 
297  ths->m_starttag_cb = starttag_cb? starttag_cb : def_starttag_cb;
298  ths->m_endtag_cb = endtag_cb? endtag_cb : def_endtag_cb;
299 }
300 
301 
302 void mrsaxparser_set_text_handler (mrsaxparser_t* ths, mrsaxparser_text_cb_t text_cb)
303 {
304  if( ths == NULL ) {
305  return;
306  }
307 
308  ths->m_text_cb = text_cb? text_cb : def_text_cb;
309 }
310 
311 
312 void mrsaxparser_parse(mrsaxparser_t* ths, const char* buf_start__)
313 {
314  char bak, *buf_start, *last_text_start, *p;
315 
316  #define MAX_ATTR 100 /* attributes per tag - a fixed border here is a security feature, not a limit */
317  char* attr[(MAX_ATTR+1)*2]; /* attributes as key/value pairs, +1 for terminating the list */
318  int free_attr[MAX_ATTR]; /* free the value at attr[i*2+1]? */
319 
320  attr[0] = NULL; /* null-terminate list, this also terminates "free_values" */
321 
322  if( ths == NULL ) {
323  return;
324  }
325 
326  buf_start = safe_strdup(buf_start__); /* we make a copy as we can easily null-terminate tag names and attributes "in place" */
327  last_text_start = buf_start;
328  p = buf_start;
329  while( *p )
330  {
331  if( *p == '<' )
332  {
333  call_text_cb(ths, last_text_start, p - last_text_start, '&'); /* flush pending text */
334 
335  p++;
336  if( strncmp(p, "!--", 3) == 0 )
337  {
338  /* skip <!-- ... --> comment
339  **************************************************************/
340 
341  p = strstr(p, "-->");
342  if( p == NULL ) { goto cleanup; }
343  p += 3;
344  }
345  else if( strncmp(p, "![CDATA[", 8) == 0 )
346  {
347  /* process <![CDATA[ ... ]]> text
348  **************************************************************/
349 
350  char* text_beg = p + 8;
351  if( (p = strstr(p, "]]>"))!=NULL ) /* `]]>` itself is not allowed in CDATA and must be escaped by dividing into two CDATA parts */ {
352  call_text_cb(ths, text_beg, p-text_beg, 'c');
353  p += 3;
354  }
355  else {
356  call_text_cb(ths, text_beg, strlen(text_beg), 'c'); /* CDATA not closed, add all remaining text */
357  goto cleanup;
358  }
359  }
360  else if( strncmp(p, "!DOCTYPE", 8) == 0 )
361  {
362  /* skip <!DOCTYPE ...> or <!DOCTYPE name [ ... ]>
363  **************************************************************/
364 
365  while( *p && *p != '[' && *p != '>' ) p++; /* search for [ or >, whatever comes first */
366  if( *p == 0 ) {
367  goto cleanup; /* unclosed doctype */
368  }
369  else if( *p == '[' ) {
370  p = strstr(p, "]>"); /* search end of inline doctype */
371  if( p == NULL ) {
372  goto cleanup; /* unclosed inline doctype */
373  }
374  else {
375  p += 2;
376  }
377  }
378  else {
379  p++;
380  }
381  }
382  else if( *p == '?' )
383  {
384  /* skip <? ... ?> processing instruction
385  **************************************************************/
386 
387  p = strstr(p, "?>");
388  if( p == NULL ) { goto cleanup; } /* unclosed processing instruction */
389  p += 2;
390  }
391  else
392  {
393  p += strspn(p, XML_WS); /* skip whitespace between `<` and tagname */
394  if( *p == '/' )
395  {
396  /* process </tag> end tag
397  **************************************************************/
398 
399  p++;
400  p += strspn(p, XML_WS); /* skip whitespace between `/` and tagname */
401  char* beg_tag_name = p;
402  p += strcspn(p, XML_WS "/>"); /* find character after tagname */
403  if( p != beg_tag_name )
404  {
405  bak = *p;
406  *p = '\0'; /* null-terminate tag name temporary, eg. a covered `>` may get important downwards */
407  mr_strlower_in_place(beg_tag_name);
408  ths->m_endtag_cb(ths->m_userdata, beg_tag_name);
409  *p = bak;
410  }
411  }
412  else
413  {
414  /* process <tag attr1="val" attr2='val' attr3=val ..>
415  **************************************************************/
416 
417  do_free_attr(attr, free_attr);
418 
419  char* beg_tag_name = p;
420  p += strcspn(p, XML_WS "/>"); /* find character after tagname */
421  if( p != beg_tag_name )
422  {
423  char* after_tag_name = p;
424 
425  /* scan for attributes */
426  int attr_index = 0;
427  while( isspace(*p) ) { p++; } /* forward to first attribute name beginning */
428  for( ; *p && *p != '/' && *p != '>'; attr_index += 2 )
429  {
430  char *beg_attr_name = p, *beg_attr_value = NULL, *beg_attr_value_new = NULL;
431 
432  p += strcspn(p, XML_WS "=/>"); /* get end of attribute name */
433  if( p != beg_attr_name )
434  {
435  /* attribute found */
436  char* after_attr_name = p;
437  p += strspn(p, XML_WS); /* skip whitespace between attribute name and possible `=` */
438  if( *p == '=' )
439  {
440  p += strspn(p, XML_WS "="); /* skip spaces and equal signs */
441  char quote = *p;
442  if( quote == '"' || quote == '\'' )
443  {
444  /* quoted attribute value */
445  p++;
446  beg_attr_value = p;
447 
448  while( *p && *p != quote ) { p++; }
449  if( *p ) {
450  *p = '\0'; /* null terminate attribute val */
451  p++;
452  }
453 
454  beg_attr_value_new = xml_decode(beg_attr_value, ' ');
455  }
456  else
457  {
458  /* unquoted attribute value, as the needed null-terminated may overwrite important characters, we'll create a copy */
459  beg_attr_value = p;
460  p += strcspn(p, XML_WS "/>"); /* get end of attribute value */
461  bak = *p;
462  *p = '\0';
463  char* temp = safe_strdup(beg_attr_value);
464  beg_attr_value_new = xml_decode(temp, ' ');
465  if( beg_attr_value_new!=temp ) { free(temp); }
466  *p = bak;
467  }
468  }
469  else
470  {
471  beg_attr_value_new = safe_strdup(NULL);
472  }
473 
474  /* add attribute */
475  if( attr_index < MAX_ATTR )
476  {
477  char* beg_attr_name_new = beg_attr_name;
478  int free_bits = (beg_attr_value_new != beg_attr_value)? FREE_VALUE : 0;
479  if( after_attr_name == p ) {
480  /* take care not to overwrite the current pointer (happens eg. for `<tag attrWithoutValue>` */
481  bak = *after_attr_name;
482  *after_attr_name = '\0';
483  beg_attr_name_new = safe_strdup(beg_attr_name);
484  *after_attr_name = bak;
485  free_bits |= FREE_KEY;
486  }
487  else {
488  *after_attr_name = '\0';
489  }
490 
491  mr_strlower_in_place(beg_attr_name_new);
492  attr[attr_index] = beg_attr_name_new;
493  attr[attr_index+1] = beg_attr_value_new;
494  attr[attr_index+2] = NULL; /* null-terminate list */
495  free_attr[attr_index>>1] = free_bits;
496  }
497  }
498 
499  while( isspace(*p) ) { p++; } /* forward to attribute name beginning */
500  }
501 
502  char bak = *after_tag_name; /* backup the character as it may be `/` or `>` which gets important downwards */
503  *after_tag_name = 0;
504  mr_strlower_in_place(beg_tag_name);
505  ths->m_starttag_cb(ths->m_userdata, beg_tag_name, attr);
506  *after_tag_name = bak;
507 
508  /* self-closing tag */
509  p += strspn(p, XML_WS); /* skip whitespace before possible `/` */
510  if( *p == '/' )
511  {
512  p++;
513  *after_tag_name = 0;
514  ths->m_endtag_cb(ths->m_userdata, beg_tag_name); /* already lowercase from starttag_cb()-call */
515  }
516  }
517 
518  } /* end of processing start-tag */
519 
520  p = strchr(p, '>');
521  if( p == NULL ) { goto cleanup; } /* unclosed start-tag or end-tag */
522  p++;
523 
524  } /* end of processing start-tag or end-tag */
525 
526  last_text_start = p;
527  }
528  else
529  {
530  p++;
531  }
532  }
533 
534  call_text_cb(ths, last_text_start, p - last_text_start, '&'); /* flush pending text */
535 
536 cleanup:
537  do_free_attr(attr, free_attr);
538  free(buf_start);
539 }
540 
- - - - diff --git a/docs/user/html/mrsimplify_8c_source.html b/docs/user/html/mrsimplify_8c_source.html deleted file mode 100644 index 957ade6a..00000000 --- a/docs/user/html/mrsimplify_8c_source.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrsimplify.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrsimplify.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include "mrmailbox.h"
26 #include "mrsimplify.h"
27 #include "mrtools.h"
28 #include "mrdehtml.h"
29 #include "mrmimeparser.h"
30 
31 
32 /*******************************************************************************
33  * Tools
34  ******************************************************************************/
35 
36 
37 static int mr_is_empty_line(const char* buf)
38 {
39  const unsigned char* p1 = (const unsigned char*)buf; /* force unsigned - otherwise the `> ' '` comparison will fail */
40  while( *p1 ) {
41  if( *p1 > ' ' ) {
42  return 0; /* at least one character found - buffer is not empty */
43  }
44  p1++;
45  }
46  return 1; /* buffer is empty or contains only spaces, tabs, lineends etc. */
47 }
48 
49 
50 static int mr_is_plain_quote(const char* buf)
51 {
52  if( buf[0] == '>' ) {
53  return 1;
54  }
55  return 0;
56 }
57 
58 
59 static int mr_is_quoted_headline(const char* buf)
60 {
61  /* This function may be called for the line _directly_ before a quote.
62  The function checks if the line contains sth. like "On 01.02.2016, xy@z wrote:" in various languages.
63  - Currently, we simply check if the last character is a ':'.
64  - Checking for the existance of an email address may fail (headlines may show the user's name instead of the address) */
65 
66  int buf_len = strlen(buf);
67 
68  if( buf_len > 80 ) {
69  return 0; /* the buffer is too long to be a quoted headline (some mailprograms (eg. "Mail" from Stock Android)
70  forget to insert a line break between the answer and the quoted headline ...)) */
71  }
72 
73  if( buf_len > 0 && buf[buf_len-1] == ':' ) {
74  return 1; /* the buffer is a quoting headline in the meaning described above) */
75  }
76 
77  return 0;
78 }
79 
80 
81 
82 /*******************************************************************************
83  * Main interface
84  ******************************************************************************/
85 
86 
87 mrsimplify_t* mrsimplify_new()
88 {
89  mrsimplify_t* ths = NULL;
90 
91  if( (ths=calloc(1, sizeof(mrsimplify_t)))==NULL ) {
92  exit(31);
93  }
94 
95  return ths;
96 }
97 
98 
99 void mrsimplify_unref(mrsimplify_t* ths)
100 {
101  if( ths == NULL ) {
102  return;
103  }
104 
105  free(ths);
106 }
107 
108 
109 /*******************************************************************************
110  * Simplify Plain Text
111  ******************************************************************************/
112 
113 
114 static void mrsimplify_simplify_plain_text(mrsimplify_t* ths, char* buf_terminated)
115 {
116  /* This function ...
117  ... removes all text after the line `-- ` (footer mark)
118  ... removes full quotes at the beginning and at the end of the text -
119  these are all lines starting with the character `>`
120  ... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */
121 
122  /* TODO: If we know, the mail is from another Messenger, we could skip most of this stuff */
123 
124  /* split the given buffer into lines */
125  carray* lines = mr_split_into_lines(buf_terminated);
126  int l, l_first = 0, l_last = carray_count(lines)-1; /* if l_last is -1, there are no lines */
127  char* line;
128 
129  /* search for the line `-- ` and ignore this and all following lines
130  If the line contains more characters, it is _not_ treated as the footer start mark (hi, Thorsten) */
131  for( l = l_first; l <= l_last; l++ )
132  {
133  line = (char*)carray_get(lines, l);
134  if( strcmp(line, "-- ")==0
135  || strcmp(line, "-- ")==0 /* quoted-printable may encode `-- ` to `-- =20` which is converted back to `-- ` ... */
136  || strcmp(line, "--")==0 /* this is not documented, but occurs frequently; however, if we get problems with this, skip this HACK */
137  || strcmp(line, "---")==0 /* - " - */
138  || strcmp(line, "----")==0 /* - " - */ )
139  {
140  l_last = l - 1; /* if l_last is -1, there are no lines */
141  break; /* done */
142  }
143  }
144 
145  /* check for "forwarding header" */
146  if( (l_last-l_first+1) >= 3 ) {
147  char* line0 = (char*)carray_get(lines, l_first);
148  char* line1 = (char*)carray_get(lines, l_first+1);
149  char* line2 = (char*)carray_get(lines, l_first+2);
150  if( strcmp(line0, "---------- Forwarded message ----------")==0 /* do not chage this! sent exactly in this form in mrchat.c! */
151  && strncmp(line1, "From: ", 6)==0
152  && line2[0] == 0 )
153  {
154  ths->m_is_forwarded = 1;
155  l_first += 3;
156  }
157  }
158 
159  /* remove lines that typically introduce a full quote (eg. `----- Original message -----` - as we do not parse the text 100%, we may
160  also loose forwarded messages, however, the user has always the option to show the full mail text. */
161  for( l = l_first; l <= l_last; l++ )
162  {
163  line = (char*)carray_get(lines, l);
164  if( strncmp(line, "-----", 5)==0
165  || strncmp(line, "_____", 5)==0
166  || strncmp(line, "=====", 5)==0
167  || strncmp(line, "*****", 5)==0
168  || strncmp(line, "~~~~~", 5)==0 )
169  {
170  l_last = l - 1; /* if l_last is -1, there are no lines */
171  break; /* done */
172  }
173  }
174 
175  /* remove full quotes at the end of the text */
176  {
177  int l_lastQuotedLine = -1;
178 
179  for( l = l_last; l >= l_first; l-- ) {
180  line = (char*)carray_get(lines, l);
181  if( mr_is_plain_quote(line) ) {
182  l_lastQuotedLine = l;
183  }
184  else if( !mr_is_empty_line(line) ) {
185  break;
186  }
187  }
188 
189  if( l_lastQuotedLine != -1 )
190  {
191  l_last = l_lastQuotedLine-1; /* if l_last is -1, there are no lines */
192 
193  if( l_last > 0 ) {
194  if( mr_is_empty_line((char*)carray_get(lines, l_last)) ) { /* allow one empty line between quote and quote headline (eg. mails from Jürgen) */
195  l_last--;
196  }
197  }
198 
199  if( l_last > 0 ) {
200  line = (char*)carray_get(lines, l_last);
201  if( mr_is_quoted_headline(line) ) {
202  l_last--;
203  }
204  }
205  }
206  }
207 
208  /* remove full quotes at the beginning of the text */
209  {
210  int l_lastQuotedLine = -1;
211  int hasQuotedHeadline = 0;
212 
213  for( l = l_first; l <= l_last; l++ ) {
214  line = (char*)carray_get(lines, l);
215  if( mr_is_plain_quote(line) ) {
216  l_lastQuotedLine = l;
217  }
218  else if( !mr_is_empty_line(line) ) {
219  if( mr_is_quoted_headline(line) && !hasQuotedHeadline && l_lastQuotedLine == -1 ) {
220  hasQuotedHeadline = 1; /* continue, the line may be a headline */
221  }
222  else {
223  break; /* non-quoting line found */
224  }
225  }
226  }
227 
228  if( l_lastQuotedLine != -1 )
229  {
230  l_first = l_lastQuotedLine + 1;
231  }
232  }
233 
234  /* re-create buffer from the remaining lines */
235  char* p1 = buf_terminated;
236  *p1 = 0; /* make sure, the string is terminated if there are no lines (l_last==-1) */
237 
238  int add_nl = 0; /* we write empty lines only in case and non-empty line follows */
239 
240  for( l = l_first; l <= l_last; l++ )
241  {
242  line = (char*)carray_get(lines, l);
243 
244  if( mr_is_empty_line(line) )
245  {
246  add_nl++;
247  }
248  else
249  {
250  if( p1 != buf_terminated ) /* flush empty lines - except if we're at the start of the buffer */
251  {
252  if( add_nl > 2 ) { add_nl = 2; } /* ignore more than one empty line (however, regard normal line ends) */
253  while( add_nl ) {
254  *p1 = '\n';
255  p1++;
256  add_nl--;
257  }
258  }
259 
260  size_t line_len = strlen(line);
261 
262  strcpy(p1, line);
263 
264  p1 = &p1[line_len]; /* points to the current terminating nullcharacters which is overwritten with the next line */
265  add_nl = 1;
266  }
267  }
268 
269  mr_free_splitted_lines(lines);
270 }
271 
272 
273 /*******************************************************************************
274  * Simplify Entry Point
275  ******************************************************************************/
276 
277 
278 char* mrsimplify_simplify(mrsimplify_t* ths, const char* in_unterminated, int in_bytes, int is_html)
279 {
280  /* create a copy of the given buffer */
281  char* out = NULL;
282 
283  if( in_unterminated == NULL || in_bytes <= 0 ) {
284  return safe_strdup("");
285  }
286 
287  out = strndup((char*)in_unterminated, in_bytes); /* strndup() makes sure, the string is null-terminated */
288  if( out == NULL ) {
289  return safe_strdup("");
290  }
291 
292  /* convert HTML to text, if needed */
293  if( is_html ) {
294  char* temp = mr_dehtml(out); /* mr_dehtml() returns way too much lineends, however they're removed in the simplification below */
295  if( temp ) {
296  free(out);
297  out = temp;
298  }
299  }
300 
301  /* simplify the text in the buffer (characters to remove may be marked by `\r`) */
302  mr_remove_cr_chars(out); /* make comparisons easier, eg. for line `-- ` */
303  mrsimplify_simplify_plain_text(ths, out);
304 
305  /* remove all `\r` from string */
306  mr_remove_cr_chars(out);
307 
308  return out;
309 }
- - - - diff --git a/docs/user/html/mrsmtp_8c_source.html b/docs/user/html/mrsmtp_8c_source.html deleted file mode 100644 index ce8bbea0..00000000 --- a/docs/user/html/mrsmtp_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrsmtp.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrsmtp.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <libetpan/libetpan.h>
24 #include "mrmailbox_internal.h"
25 #include "mrsmtp.h"
26 
27 
28 #ifndef DEBUG_SMTP
29 #define DEBUG_SMTP 0
30 #endif
31 
32 
33 #define LOCK_SMTP pthread_mutex_lock(&ths->m_mutex); smtp_locked = 1;
34 #define UNLOCK_SMTP if( smtp_locked ) { pthread_mutex_unlock(&ths->m_mutex); smtp_locked = 0; }
35 
36 
37 /*******************************************************************************
38  * Main interface
39  ******************************************************************************/
40 
41 
42 mrsmtp_t* mrsmtp_new(mrmailbox_t* mailbox)
43 {
44  mrsmtp_t* ths;
45  if( (ths=calloc(1, sizeof(mrsmtp_t)))==NULL ) {
46  exit(29);
47  }
48 
49  ths->m_log_connect_errors = 1;
50 
51  ths->m_mailbox = mailbox; /* should be used for logging only */
52  pthread_mutex_init(&ths->m_mutex, NULL);
53  return ths;
54 }
55 
56 
57 void mrsmtp_unref(mrsmtp_t* ths)
58 {
59  if( ths == NULL ) {
60  return;
61  }
62  mrsmtp_disconnect(ths);
63  pthread_mutex_destroy(&ths->m_mutex);
64  free(ths->m_from);
65  free(ths);
66 }
67 
68 
69 /*******************************************************************************
70  * Connect/Disconnect
71  ******************************************************************************/
72 
73 
74 int mrsmtp_is_connected(const mrsmtp_t* ths)
75 {
76  return (ths && ths->m_hEtpan)? 1 : 0;
77 }
78 
79 
80 static void body_progress(size_t current, size_t maximum, void* user_data)
81 {
82  #if DEBUG_SMTP
83  printf("body_progress called with current=%i, maximum=%i.", (int)current, (int)maximum);
84  #endif
85 }
86 
87 
88 #if DEBUG_SMTP
89 static void logger(mailsmtp* smtp, int log_type, const char* buffer__, size_t size, void* user_data)
90 {
91  char* buffer = malloc(size+1);
92  memcpy(buffer, buffer__, size);
93  buffer[size] = 0;
94  printf("SMPT: %i: %s", log_type, buffer__);
95 }
96 #endif
97 
98 
99 int mrsmtp_connect(mrsmtp_t* ths, const mrloginparam_t* lp)
100 {
101  int success = 0, smtp_locked = 0;
102  int r, try_esmtp;
103 
104  if( ths == NULL || lp == NULL ) {
105  return 0;
106  }
107 
108  LOCK_SMTP
109 
110  if( ths->m_mailbox->m_cb(ths->m_mailbox, MR_EVENT_IS_ONLINE, 0, 0)!=1 ) {
111  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, MR_ERR_NONETWORK, NULL);
112  goto cleanup;
113  }
114 
115  if( ths->m_hEtpan ) {
116  mrmailbox_log_warning(ths->m_mailbox, 0, "Already connected to SMTP server.");
117  success = 1; /* otherwise, the handle would get deleted */
118  goto cleanup;
119  }
120 
121  if( lp->m_addr == NULL || lp->m_send_server == NULL || lp->m_send_port == 0 ) {
122  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "Cannot connect to SMTP; bad parameters.");
123  goto cleanup;
124  }
125 
126  free(ths->m_from);
127  ths->m_from = safe_strdup(lp->m_addr);
128 
129  ths->m_hEtpan = mailsmtp_new(0, NULL);
130  if( ths->m_hEtpan == NULL ) {
131  mrmailbox_log_error(ths->m_mailbox, 0, "SMTP-object creationed failed.");
132  goto cleanup;
133  }
134  mailsmtp_set_progress_callback(ths->m_hEtpan, body_progress, ths);
135  #if DEBUG_SMTP
136  mailsmtp_set_logger(ths->m_hEtpan, logger, ths);
137  #endif
138 
139  /* connect to SMTP server */
140  if( lp->m_server_flags&(MR_SMTP_SOCKET_STARTTLS|MR_SMTP_SOCKET_PLAIN) )
141  {
142  mrmailbox_log_info(ths->m_mailbox, 0, "Connecting to SMTP-server \"%s:%i\"...", lp->m_send_server, (int)lp->m_send_port);
143  if( (r=mailsmtp_socket_connect(ths->m_hEtpan, lp->m_send_server, lp->m_send_port)) != MAILSMTP_NO_ERROR ) {
144  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-STARTTLS connection to %s:%i failed (%s)", lp->m_send_server, (int)lp->m_send_port, mailsmtp_strerror(r));
145  goto cleanup;
146  }
147  }
148  else
149  {
150  mrmailbox_log_info(ths->m_mailbox, 0, "Connecting to SMTP-server \"%s:%i\" via SSL...", lp->m_send_server, (int)lp->m_send_port);
151  if( (r=mailsmtp_ssl_connect(ths->m_hEtpan, lp->m_send_server, lp->m_send_port)) != MAILSMTP_NO_ERROR ) {
152  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMPT-SSL connection to %s:%i failed (%s)", lp->m_send_server, (int)lp->m_send_port, mailsmtp_strerror(r));
153  goto cleanup;
154  }
155  }
156 
157  try_esmtp = 1;
158  ths->m_esmtp = 0;
159  if( try_esmtp && (r=mailesmtp_ehlo(ths->m_hEtpan))==MAILSMTP_NO_ERROR ) {
160  ths->m_esmtp = 1;
161  }
162  else if( !try_esmtp || r==MAILSMTP_ERROR_NOT_IMPLEMENTED ) {
163  r = mailsmtp_helo(ths->m_hEtpan);
164  }
165 
166  if( r != MAILSMTP_NO_ERROR ) {
167  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-helo failed (%s)", mailsmtp_strerror(r));
168  goto cleanup;
169  }
170 
171  if( lp->m_server_flags&MR_SMTP_SOCKET_STARTTLS )
172  {
173  mrmailbox_log_info(ths->m_mailbox, 0, "Switching to SMTP-STARTTLS.");
174  if( (r=mailsmtp_socket_starttls(ths->m_hEtpan)) != MAILSMTP_NO_ERROR ) {
175  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-STARTTLS failed (%s)", mailsmtp_strerror(r));
176  goto cleanup;
177  }
178 
179  ths->m_esmtp = 0;
180  if( try_esmtp && (r=mailesmtp_ehlo(ths->m_hEtpan))==MAILSMTP_NO_ERROR ) {
181  ths->m_esmtp = 1;
182  }
183  else if( !try_esmtp || r==MAILSMTP_ERROR_NOT_IMPLEMENTED ) {
184  r = mailsmtp_helo(ths->m_hEtpan);
185  }
186 
187  if (r != MAILSMTP_NO_ERROR) {
188  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-helo failed (%s)", mailsmtp_strerror(r));
189  goto cleanup;
190  }
191  }
192  mrmailbox_log_info(ths->m_mailbox, 0, "Connection to SMTP-server ok.");
193 
194  if( lp->m_send_user )
195  {
196  mrmailbox_log_info(ths->m_mailbox, 0, "Login to SMTP-server as \"%s\"...", lp->m_send_user);
197 
198  if((r=mailsmtp_auth(ths->m_hEtpan, lp->m_send_user, lp->m_send_pw))!=MAILSMTP_NO_ERROR ) {
199  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-login failed for user %s (%s)", lp->m_send_user, mailsmtp_strerror(r));
200  goto cleanup;
201  }
202 
203  mrmailbox_log_info(ths->m_mailbox, 0, "SMTP-Login ok.");
204  }
205 
206  success = 1;
207 
208 cleanup:
209  if( !success ) {
210  if( ths->m_hEtpan ) {
211  mailsmtp_free(ths->m_hEtpan);
212  ths->m_hEtpan = NULL;
213  }
214  }
215 
216  UNLOCK_SMTP
217 
218  return success;
219 }
220 
221 
222 void mrsmtp_disconnect(mrsmtp_t* ths)
223 {
224  int smtp_locked = 0;
225 
226  if( ths == NULL ) {
227  return;
228  }
229 
230  LOCK_SMTP
231 
232  if( ths->m_hEtpan ) {
233  //mailsmtp_quit(ths->m_hEtpan); -- ?
234  mailsmtp_free(ths->m_hEtpan);
235  ths->m_hEtpan = NULL;
236  }
237 
238  UNLOCK_SMTP
239 }
240 
241 
242 /*******************************************************************************
243  * Send a message
244  ******************************************************************************/
245 
246 
247 int mrsmtp_send_msg(mrsmtp_t* ths, const clist* recipients, const char* data_not_terminated, size_t data_bytes)
248 {
249  int success = 0, r, smtp_locked = 0;
250  clistiter* iter;
251 
252  if( ths == NULL ) {
253  return 0;
254  }
255 
256  if( recipients == NULL || clist_count(recipients)==0 || data_not_terminated == NULL || data_bytes == 0 ) {
257  return 1; /* "null message" send */
258  }
259 
260  LOCK_SMTP
261 
262  if( ths->m_hEtpan==NULL ) {
263  goto cleanup;
264  }
265 
266  /* set source */
267  if( (r=(ths->m_esmtp?
268  mailesmtp_mail(ths->m_hEtpan, ths->m_from, 1, "etPanSMTPTest") :
269  mailsmtp_mail(ths->m_hEtpan, ths->m_from))) != MAILSMTP_NO_ERROR )
270  {
271  // this error is very usual - we've simply lost the server connection and reconnect as soon as possible.
272  // so, we do not log the first time this happens
273  mrmailbox_log_error_if(&ths->m_log_usual_error, ths->m_mailbox, 0, "mailsmtp_mail: %s, %s (%i)", ths->m_from, mailsmtp_strerror(r), (int)r);
274  ths->m_log_usual_error = 1;
275  goto cleanup;
276  }
277 
278  ths->m_log_usual_error = 0;
279 
280  /* set recipients */
281  for( iter=clist_begin(recipients); iter!=NULL; iter=clist_next(iter)) {
282  const char* rcpt = clist_content(iter);
283  if( (r = (ths->m_esmtp?
284  mailesmtp_rcpt(ths->m_hEtpan, rcpt, MAILSMTP_DSN_NOTIFY_FAILURE|MAILSMTP_DSN_NOTIFY_DELAY, NULL) :
285  mailsmtp_rcpt(ths->m_hEtpan, rcpt))) != MAILSMTP_NO_ERROR) {
286  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "mailsmtp_rcpt: %s: %s", rcpt, mailsmtp_strerror(r));
287  goto cleanup;
288  }
289  }
290 
291  /* message */
292  if ((r = mailsmtp_data(ths->m_hEtpan)) != MAILSMTP_NO_ERROR) {
293  fprintf(stderr, "mailsmtp_data: %s\n", mailsmtp_strerror(r));
294  goto cleanup;
295  }
296 
297  if ((r = mailsmtp_data_message(ths->m_hEtpan, data_not_terminated, data_bytes)) != MAILSMTP_NO_ERROR) {
298  fprintf(stderr, "mailsmtp_data_message: %s\n", mailsmtp_strerror(r));
299  goto cleanup;
300  }
301 
302  success = 1;
303 
304 cleanup:
305 
306  UNLOCK_SMTP
307 
308  return success;
309 }
310 
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrsqlite3_8c_source.html b/docs/user/html/mrsqlite3_8c_source.html deleted file mode 100644 index 8356a330..00000000 --- a/docs/user/html/mrsqlite3_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrsqlite3.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrsqlite3.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 
25 
26 /* This class wraps around SQLite. Some hints to the underlying database:
27 
28 - `PRAGMA cache_size` and `PRAGMA page_size`: As we save BLOBs in external
29  files, caching is not that important; we rely on the system defaults here
30  (normally 2 MB cache, 1 KB page size on sqlite < 3.12.0, 4 KB for newer
31  versions)
32 
33 - We use `sqlite3_last_insert_rowid()` to find out created records - for this
34  purpose, the primary ID has to be marked using `INTEGER PRIMARY KEY`, see
35  https://www.sqlite.org/c3ref/last_insert_rowid.html
36 
37 - Some words to the "param" fields: These fields contains a string with
38  additonal, named parameters which must not be accessed by a search and/or
39  are very seldomly used. Moreover, this allows smart minor database updates. */
40 
41 
42 /*******************************************************************************
43  * Tools
44  ******************************************************************************/
45 
46 
47 void mrsqlite3_log_error(mrsqlite3_t* ths, const char* msg_format, ...)
48 {
49  char* msg;
50  const char* notSetUp = "SQLite object not set up.";
51  va_list va;
52 
53  va_start(va, msg_format);
54  msg = sqlite3_vmprintf(msg_format, va); if( msg == NULL ) { mrmailbox_log_error(ths->m_mailbox, 0, "Bad log format string \"%s\".", msg_format); }
55  mrmailbox_log_error(ths->m_mailbox, 0, "%s SQLite says: %s", msg, ths->m_cobj? sqlite3_errmsg(ths->m_cobj) : notSetUp);
56  sqlite3_free(msg);
57  va_end(va);
58 }
59 
60 
61 sqlite3_stmt* mrsqlite3_prepare_v2_(mrsqlite3_t* ths, const char* querystr)
62 {
63  sqlite3_stmt* retStmt = NULL;
64 
65  if( ths == NULL || querystr == NULL || ths->m_cobj == NULL ) {
66  return NULL;
67  }
68 
69  if( sqlite3_prepare_v2(ths->m_cobj,
70  querystr, -1 /*read `sql` up to the first null-byte*/,
71  &retStmt,
72  NULL /*tail not interesing, we use only single statements*/) != SQLITE_OK )
73  {
74  mrsqlite3_log_error(ths, "Query failed: %s", querystr);
75  return NULL;
76  }
77 
78  /* success - the result mus be freed using sqlite3_finalize() */
79  return retStmt;
80 }
81 
82 
83 int mrsqlite3_execute__(mrsqlite3_t* ths, const char* querystr)
84 {
85  int success = 0;
86  sqlite3_stmt* stmt = NULL;
87  int sqlState;
88 
89  stmt = mrsqlite3_prepare_v2_(ths, querystr);
90  if( stmt == NULL ) {
91  goto cleanup;
92  }
93 
94  sqlState = sqlite3_step(stmt);
95  if( sqlState != SQLITE_DONE && sqlState != SQLITE_ROW ) {
96  mrsqlite3_log_error(ths, "Cannot excecute \"%s\".", querystr);
97  goto cleanup;
98  }
99 
100  success = 1;
101 
102 cleanup:
103  if( stmt ) {
104  sqlite3_finalize(stmt);
105  }
106  return success;
107 }
108 
109 
110 /*******************************************************************************
111  * Main interface
112  ******************************************************************************/
113 
114 
115 mrsqlite3_t* mrsqlite3_new(mrmailbox_t* mailbox)
116 {
117  mrsqlite3_t* ths = NULL;
118  int i;
119 
120  if( (ths=calloc(1, sizeof(mrsqlite3_t)))==NULL ) {
121  exit(24); /* cannot allocate little memory, unrecoverable error */
122  }
123 
124  ths->m_mailbox = mailbox;
125 
126  for( i = 0; i < PREDEFINED_CNT; i++ ) {
127  ths->m_pd[i] = NULL;
128  }
129 
130  pthread_mutex_init(&ths->m_critical_, NULL);
131 
132  return ths;
133 }
134 
135 
136 void mrsqlite3_unref(mrsqlite3_t* ths)
137 {
138  if( ths == NULL ) {
139  return;
140  }
141 
142  if( ths->m_cobj ) {
143  pthread_mutex_lock(&ths->m_critical_); /* as a very exeception, we do the locking inside the mrsqlite3-class - normally, this should be done by the caller! */
144  mrsqlite3_close__(ths);
145  pthread_mutex_unlock(&ths->m_critical_);
146  }
147 
148  pthread_mutex_destroy(&ths->m_critical_);
149  free(ths);
150 }
151 
152 
153 int mrsqlite3_open__(mrsqlite3_t* ths, const char* dbfile, int flags)
154 {
155  if( ths == NULL || dbfile == NULL ) {
156  goto cleanup;
157  }
158 
159  if( ths->m_cobj ) {
160  mrmailbox_log_error(ths->m_mailbox, 0, "Cannot open, database \"%s\" already opend.", dbfile);
161  goto cleanup;
162  }
163 
164  if( sqlite3_open(dbfile, &ths->m_cobj) != SQLITE_OK ) {
165  mrsqlite3_log_error(ths, "Cannot open database \"%s\".", dbfile); /* ususally, even for errors, the pointer is set up (if not, this is also checked by mrsqlite3_log_error()) */
166  goto cleanup;
167  }
168 
169  if( !(flags&MR_OPEN_READONLY) )
170  {
171  /* Init tables to dbversion=0 */
172  if( !mrsqlite3_table_exists__(ths, "config") )
173  {
174  mrmailbox_log_info(ths->m_mailbox, 0, "First time init: creating tables in \"%s\".", dbfile);
175 
176  mrsqlite3_execute__(ths, "CREATE TABLE config (id INTEGER PRIMARY KEY, keyname TEXT, value TEXT);");
177  mrsqlite3_execute__(ths, "CREATE INDEX config_index1 ON config (keyname);");
178 
179  mrsqlite3_execute__(ths, "CREATE TABLE contacts (id INTEGER PRIMARY KEY,"
180  " name TEXT DEFAULT '',"
181  " addr TEXT DEFAULT '' COLLATE NOCASE,"
182  " origin INTEGER DEFAULT 0,"
183  " blocked INTEGER DEFAULT 0,"
184  " last_seen INTEGER DEFAULT 0," /* last_seen is for future use */
185  " param TEXT DEFAULT '');"); /* param is for future use, eg. for the status */
186  mrsqlite3_execute__(ths, "CREATE INDEX contacts_index1 ON contacts (name COLLATE NOCASE);"); /* needed for query contacts */
187  mrsqlite3_execute__(ths, "CREATE INDEX contacts_index2 ON contacts (addr COLLATE NOCASE);"); /* needed for query and on receiving mails */
188  mrsqlite3_execute__(ths, "INSERT INTO contacts (id,name,origin) VALUES (1,'self',262144), (2,'system',262144), (3,'rsvd',262144), (4,'rsvd',262144), (5,'rsvd',262144), (6,'rsvd',262144), (7,'rsvd',262144), (8,'rsvd',262144), (9,'rsvd',262144);");
189  #if !defined(MR_ORIGIN_INTERNAL) || MR_ORIGIN_INTERNAL!=262144
190  #error
191  #endif
192 
193  mrsqlite3_execute__(ths, "CREATE TABLE chats (id INTEGER PRIMARY KEY, "
194  " type INTEGER DEFAULT 0,"
195  " name TEXT DEFAULT '',"
196  " draft_timestamp INTEGER DEFAULT 0,"
197  " draft_txt TEXT DEFAULT '',"
198  " blocked INTEGER DEFAULT 0,"
199  " grpid TEXT DEFAULT ''," /* contacts-global unique group-ID, see mrchat.c for details */
200  " param TEXT DEFAULT '');");
201  mrsqlite3_execute__(ths, "CREATE INDEX chats_index1 ON chats (grpid);");
202  mrsqlite3_execute__(ths, "CREATE TABLE chats_contacts (chat_id INTEGER, contact_id INTEGER);");
203  mrsqlite3_execute__(ths, "CREATE INDEX chats_contacts_index1 ON chats_contacts (chat_id);"); /* the other way round, an index on contact_id is only needed for blocking users */
204  #if !defined(MR_CHAT_TYPE_NORMAL) || MR_CHAT_TYPE_NORMAL!=100 || MR_CHAT_TYPE_GROUP!=120 || MR_CHAT_ID_DEADDROP!=1 || MR_CHAT_ID_TO_DEADDROP!=2 || MR_CHAT_ID_TRASH!=3 || MR_CHAT_ID_MSGS_IN_CREATION!=4 || MR_CHAT_ID_STARRED!=5 || MR_CHAT_ID_ARCHIVED_LINK!=6
205  mrsqlite3_execute__(ths, "INSERT INTO chats (id,type,name) VALUES (1,120,'deaddrop'), (2,120,'to_deaddrop'), (3,120,'trash'), (4,120,'msgs_in_creation'), (5,120,'starred'), (6,120,'archivedlink'), (7,100,'rsvd'), (8,100,'rsvd'), (9,100,'rsvd');");
206  #error
207  #endif
208 
209  mrsqlite3_execute__(ths, "CREATE TABLE msgs (id INTEGER PRIMARY KEY,"
210  " rfc724_mid TEXT DEFAULT ''," /* forever-global-unique Message-ID-string, unfortunately, this cannot be easily used to communicate via IMAP */
211  " server_folder TEXT DEFAULT ''," /* folder as used on the server, the folder will change when messages are moved around. */
212  " server_uid INTEGER DEFAULT 0," /* UID as used on the server, the UID will change when messages are moved around, unique together with validity, see RFC 3501; the validity may differ from folder to folder. We use the server_uid for "markseen" and to delete messages as we check against the message-id, we ignore the validity for these commands. */
213  " chat_id INTEGER DEFAULT 0,"
214  " from_id INTEGER DEFAULT 0,"
215  " to_id INTEGER DEFAULT 0," /* to_id is needed to allow moving messages eg. from "deaddrop" to a normal chat, may be unset */
216  " timestamp INTEGER DEFAULT 0,"
217  " type INTEGER DEFAULT 0,"
218  " state INTEGER DEFAULT 0,"
219  " msgrmsg INTEGER DEFAULT 1," /* does the message come from a messenger? (0=no, 1=yes, 2=no, but the message is a reply to a messenger message) */
220  " bytes INTEGER DEFAULT 0," /* not used, added in ~ v0.1.12 */
221  " txt TEXT DEFAULT ''," /* as this is also used for (fulltext) searching, nothing but normal, plain text should go here */
222  " txt_raw TEXT DEFAULT '',"
223  " param TEXT DEFAULT '');");
224  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index1 ON msgs (rfc724_mid);"); /* in our database, one email may be split up to several messages (eg. one per image), so the email-Message-ID may be used for several records; id is always unique */
225  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index2 ON msgs (chat_id);");
226  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index3 ON msgs (timestamp);"); /* for sorting */
227  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index4 ON msgs (state);"); /* for selecting the count of fresh messages (as there are normally only few unread messages, an index over the chat_id is not required for _this_ purpose */
228  mrsqlite3_execute__(ths, "INSERT INTO msgs (id,msgrmsg,txt) VALUES (1,0,'marker1'), (2,0,'rsvd'), (3,0,'rsvd'), (4,0,'rsvd'), (5,0,'rsvd'), (6,0,'rsvd'), (7,0,'rsvd'), (8,0,'rsvd'), (9,0,'daymarker');"); /* make sure, the reserved IDs are not used */
229 
230  mrsqlite3_execute__(ths, "CREATE TABLE jobs (id INTEGER PRIMARY KEY,"
231  " added_timestamp INTEGER,"
232  " desired_timestamp INTEGER DEFAULT 0,"
233  " action INTEGER,"
234  " foreign_id INTEGER,"
235  " param TEXT DEFAULT '');");
236  mrsqlite3_execute__(ths, "CREATE INDEX jobs_index1 ON jobs (desired_timestamp);");
237 
238  if( !mrsqlite3_table_exists__(ths, "config") || !mrsqlite3_table_exists__(ths, "contacts")
239  || !mrsqlite3_table_exists__(ths, "chats") || !mrsqlite3_table_exists__(ths, "chats_contacts")
240  || !mrsqlite3_table_exists__(ths, "msgs") || !mrsqlite3_table_exists__(ths, "jobs") )
241  {
242  mrsqlite3_log_error(ths, "Cannot create tables in new database \"%s\".", dbfile);
243  goto cleanup; /* cannot create the tables - maybe we cannot write? */
244  }
245 
246  mrsqlite3_set_config_int__(ths, "dbversion", 0);
247  }
248 
249  /* Update database */
250  int dbversion = mrsqlite3_get_config_int__(ths, "dbversion", 0);
251  #define NEW_DB_VERSION 1
252  if( dbversion < NEW_DB_VERSION )
253  {
254  mrsqlite3_execute__(ths, "CREATE TABLE leftgrps ("
255  " id INTEGER PRIMARY KEY,"
256  " grpid TEXT DEFAULT '');");
257  mrsqlite3_execute__(ths, "CREATE INDEX leftgrps_index1 ON leftgrps (grpid);");
258 
259  dbversion = NEW_DB_VERSION;
260  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
261  }
262  #undef NEW_DB_VERSION
263 
264  #define NEW_DB_VERSION 2
265  if( dbversion < NEW_DB_VERSION )
266  {
267  mrsqlite3_execute__(ths, "ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT '';");
268 
269  dbversion = NEW_DB_VERSION;
270  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
271  }
272  #undef NEW_DB_VERSION
273 
274  #define NEW_DB_VERSION 7
275  if( dbversion < NEW_DB_VERSION )
276  {
277  mrsqlite3_execute__(ths, "CREATE TABLE keypairs ("
278  " id INTEGER PRIMARY KEY,"
279  " addr TEXT DEFAULT '' COLLATE NOCASE,"
280  " is_default INTEGER DEFAULT 0,"
281  " private_key,"
282  " public_key,"
283  " created INTEGER DEFAULT 0);");
284 
285  dbversion = NEW_DB_VERSION;
286  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
287  }
288  #undef NEW_DB_VERSION
289 
290  #define NEW_DB_VERSION 10
291  if( dbversion < NEW_DB_VERSION )
292  {
293  mrsqlite3_execute__(ths, "CREATE TABLE acpeerstates ("
294  " id INTEGER PRIMARY KEY,"
295  " addr TEXT DEFAULT '' COLLATE NOCASE," /* no UNIQUE here, Autocrypt: requires the index above mail+type (type however, is not used at the moment, but to be future-proof, we do not use an index. instead we just check ourself if there is a record or not)*/
296  " last_seen INTEGER DEFAULT 0,"
297  " last_seen_autocrypt INTEGER DEFAULT 0,"
298  " public_key,"
299  " prefer_encrypted INTEGER DEFAULT 0);");
300  mrsqlite3_execute__(ths, "CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);");
301 
302  dbversion = NEW_DB_VERSION;
303  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
304  }
305  #undef NEW_DB_VERSION
306 
307  #define NEW_DB_VERSION 12
308  if( dbversion < NEW_DB_VERSION )
309  {
310  mrsqlite3_execute__(ths, "CREATE TABLE msgs_mdns ("
311  " msg_id INTEGER, "
312  " contact_id INTEGER);");
313  mrsqlite3_execute__(ths, "CREATE INDEX msgs_mdns_index1 ON msgs_mdns (msg_id);");
314 
315  dbversion = NEW_DB_VERSION;
316  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
317  }
318  #undef NEW_DB_VERSION
319 
320  #define NEW_DB_VERSION 17
321  if( dbversion < NEW_DB_VERSION )
322  {
323  mrsqlite3_execute__(ths, "ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;");
324  mrsqlite3_execute__(ths, "CREATE INDEX chats_index2 ON chats (archived);");
325  mrsqlite3_execute__(ths, "ALTER TABLE msgs ADD COLUMN starred INTEGER DEFAULT 0;");
326  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index5 ON msgs (starred);");
327 
328  dbversion = NEW_DB_VERSION;
329  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
330  }
331  #undef NEW_DB_VERSION
332  }
333 
334  mrmailbox_log_info(ths->m_mailbox, 0, "Opened \"%s\" successfully.", dbfile);
335  return 1;
336 
337 cleanup:
338  mrsqlite3_close__(ths);
339  return 0;
340 }
341 
342 
343 void mrsqlite3_close__(mrsqlite3_t* ths)
344 {
345  int i;
346 
347  if( ths == NULL ) {
348  return;
349  }
350 
351  if( ths->m_cobj )
352  {
353  for( i = 0; i < PREDEFINED_CNT; i++ ) {
354  if( ths->m_pd[i] ) {
355  sqlite3_finalize(ths->m_pd[i]);
356  ths->m_pd[i] = NULL;
357  }
358  }
359 
360  sqlite3_close(ths->m_cobj);
361  ths->m_cobj = NULL;
362  }
363 
364  mrmailbox_log_info(ths->m_mailbox, 0, "Database closed."); /* We log the information even if not real closing took place; this is to detect logic errors. */
365 }
366 
367 
368 int mrsqlite3_is_open(const mrsqlite3_t* ths)
369 {
370  if( ths == NULL || ths->m_cobj == NULL ) {
371  return 0;
372  }
373  return 1;
374 }
375 
376 
377 sqlite3_stmt* mrsqlite3_predefine__(mrsqlite3_t* ths, size_t idx, const char* querystr)
378 {
379  /* predefines a statement or resets and reuses a statment.
380  Subsequent call may ommit the querystring.
381  CAVE: you must not call this function with different strings for the same index! */
382 
383  if( ths == NULL || ths->m_cobj == NULL || idx >= PREDEFINED_CNT ) {
384  return NULL;
385  }
386 
387  if( ths->m_pd[idx] ) {
388  sqlite3_reset(ths->m_pd[idx]);
389  return ths->m_pd[idx]; /* fine, already prepared before */
390  }
391 
392  /*prepare for the first time - this requires the querystring*/
393  if( querystr == NULL ) {
394  return NULL;
395  }
396 
397  if( sqlite3_prepare_v2(ths->m_cobj,
398  querystr, -1 /*read `sql` up to the first null-byte*/,
399  &ths->m_pd[idx],
400  NULL /*tail not interesing, we use only single statements*/) != SQLITE_OK )
401  {
402  mrsqlite3_log_error(ths, "Preparing statement \"%s\" failed.", querystr);
403  return NULL;
404  }
405 
406  return ths->m_pd[idx];
407 }
408 
409 
410 void mrsqlite3_reset_all_predefinitions(mrsqlite3_t* ths)
411 {
412  int i;
413  for( i = 0; i < PREDEFINED_CNT; i++ ) {
414  if( ths->m_pd[i] ) {
415  sqlite3_reset(ths->m_pd[i]);
416  }
417  }
418 }
419 
420 
421 int mrsqlite3_table_exists__(mrsqlite3_t* ths, const char* name)
422 {
423  int ret = 0;
424  char* querystr = NULL;
425  sqlite3_stmt* stmt = NULL;
426  int sqlState;
427 
428  if( (querystr=sqlite3_mprintf("PRAGMA table_info(%s)", name)) == NULL ) { /* this statement cannot be used with binded variables */
429  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_table_exists_(): Out of memory.");
430  goto cleanup;
431  }
432 
433  if( (stmt=mrsqlite3_prepare_v2_(ths, querystr)) == NULL ) {
434  goto cleanup;
435  }
436 
437  sqlState = sqlite3_step(stmt);
438  if( sqlState == SQLITE_ROW ) {
439  ret = 1; /* the table exists. Other states are SQLITE_DONE or SQLITE_ERROR in both cases we return 0. */
440  }
441 
442  /* success - fall through to free allocated objects */
443  ;
444 
445  /* error/cleanup */
446 cleanup:
447  if( stmt ) {
448  sqlite3_finalize(stmt);
449  }
450 
451  if( querystr ) {
452  sqlite3_free(querystr);
453  }
454 
455  return ret;
456 }
457 
458 
459 /*******************************************************************************
460  * Handle configuration
461  ******************************************************************************/
462 
463 
464 int mrsqlite3_set_config__(mrsqlite3_t* ths, const char* key, const char* value)
465 {
466  int state;
467  sqlite3_stmt* stmt;
468 
469  if( key == NULL ) {
470  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Bad parameter.");
471  return 0;
472  }
473 
474  if( !mrsqlite3_is_open(ths) ) {
475  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Database not ready.");
476  return 0;
477  }
478 
479  if( value )
480  {
481  /* insert/update key=value */
482  #define SELECT_v_FROM_config_k_STATEMENT "SELECT value FROM config WHERE keyname=?;"
483  stmt = mrsqlite3_predefine__(ths, SELECT_v_FROM_config_k, SELECT_v_FROM_config_k_STATEMENT);
484  sqlite3_bind_text (stmt, 1, key, -1, SQLITE_STATIC);
485  state=sqlite3_step(stmt);
486  if( state == SQLITE_DONE ) {
487  stmt = mrsqlite3_predefine__(ths, INSERT_INTO_config_kv, "INSERT INTO config (keyname, value) VALUES (?, ?);");
488  sqlite3_bind_text (stmt, 1, key, -1, SQLITE_STATIC);
489  sqlite3_bind_text (stmt, 2, value, -1, SQLITE_STATIC);
490  state=sqlite3_step(stmt);
491 
492  }
493  else if( state == SQLITE_ROW ) {
494  stmt = mrsqlite3_predefine__(ths, UPDATE_config_vk, "UPDATE config SET value=? WHERE keyname=?;");
495  sqlite3_bind_text (stmt, 1, value, -1, SQLITE_STATIC);
496  sqlite3_bind_text (stmt, 2, key, -1, SQLITE_STATIC);
497  state=sqlite3_step(stmt);
498  }
499  else {
500  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Cannot read value.");
501  return 0;
502  }
503  }
504  else
505  {
506  /* delete key */
507  stmt = mrsqlite3_predefine__(ths, DELETE_FROM_config_k, "DELETE FROM config WHERE keyname=?;");
508  sqlite3_bind_text (stmt, 1, key, -1, SQLITE_STATIC);
509  state=sqlite3_step(stmt);
510  }
511 
512  if( state != SQLITE_DONE ) {
513  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Cannot change value.");
514  return 0;
515  }
516 
517  return 1;
518 }
519 
520 
521 char* mrsqlite3_get_config__(mrsqlite3_t* ths, const char* key, const char* def) /* the returned string must be free()'d, NULL is only returned if def is NULL */
522 {
523  sqlite3_stmt* stmt;
524 
525  if( !mrsqlite3_is_open(ths) || key == NULL ) {
526  return strdup_keep_null(def);
527  }
528 
529  stmt = mrsqlite3_predefine__(ths, SELECT_v_FROM_config_k, SELECT_v_FROM_config_k_STATEMENT);
530  sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
531  if( sqlite3_step(stmt) == SQLITE_ROW )
532  {
533  const unsigned char* ptr = sqlite3_column_text(stmt, 0); /* Do not pass the pointers returned from sqlite3_column_text(), etc. into sqlite3_free(). */
534  if( ptr )
535  {
536  /* success, fall through below to free objects */
537  return safe_strdup((const char*)ptr);
538  }
539  }
540 
541  /* return the default value */
542  return strdup_keep_null(def);
543 }
544 
545 
546 int32_t mrsqlite3_get_config_int__(mrsqlite3_t* ths, const char* key, int32_t def)
547 {
548  char* str = mrsqlite3_get_config__(ths, key, NULL);
549  if( str == NULL ) {
550  return def;
551  }
552  int32_t ret = atol(str);
553  free(str);
554  return ret;
555 }
556 
557 
558 int mrsqlite3_set_config_int__(mrsqlite3_t* ths, const char* key, int32_t value)
559 {
560  char* value_str = mr_mprintf("%i", (int)value);
561  if( value_str == NULL ) {
562  return 0;
563  }
564  int ret = mrsqlite3_set_config__(ths, key, value_str);
565  free(value_str);
566  return ret;
567 }
568 
569 
570 /*******************************************************************************
571  * Locking
572  ******************************************************************************/
573 
574 
575 void mrsqlite3_lock(mrsqlite3_t* ths) /* wait and lock */
576 {
577  pthread_mutex_lock(&ths->m_critical_);
578 
579  mrmailbox_wake_lock(ths->m_mailbox);
580 }
581 
582 
583 void mrsqlite3_unlock(mrsqlite3_t* ths)
584 {
585  mrmailbox_wake_unlock(ths->m_mailbox);
586 
587  pthread_mutex_unlock(&ths->m_critical_);
588 }
589 
590 
591 /*******************************************************************************
592  * Transactions
593  ******************************************************************************/
594 
595 
596 void mrsqlite3_begin_transaction__(mrsqlite3_t* ths)
597 {
598  sqlite3_stmt* stmt;
599 
600  ths->m_transactionCount++; /* this is safe, as the database should be locked when using a transaction */
601 
602  if( ths->m_transactionCount == 1 )
603  {
604  stmt = mrsqlite3_predefine__(ths, BEGIN_transaction, "BEGIN;");
605  if( sqlite3_step(stmt) != SQLITE_DONE ) {
606  mrsqlite3_log_error(ths, "Cannot begin transaction.");
607  }
608  }
609 }
610 
611 
612 void mrsqlite3_rollback__(mrsqlite3_t* ths)
613 {
614  sqlite3_stmt* stmt;
615 
616  if( ths->m_transactionCount >= 1 )
617  {
618  if( ths->m_transactionCount == 1 )
619  {
620  stmt = mrsqlite3_predefine__(ths, ROLLBACK_transaction, "ROLLBACK;");
621  if( sqlite3_step(stmt) != SQLITE_DONE ) {
622  mrsqlite3_log_error(ths, "Cannot rollback transaction.");
623  }
624  }
625 
626  ths->m_transactionCount--;
627  }
628 }
629 
630 
631 void mrsqlite3_commit__(mrsqlite3_t* ths)
632 {
633  sqlite3_stmt* stmt;
634 
635  if( ths->m_transactionCount >= 1 )
636  {
637  if( ths->m_transactionCount == 1 )
638  {
639  stmt = mrsqlite3_predefine__(ths, COMMIT_transaction, "COMMIT;");
640  if( sqlite3_step(stmt) != SQLITE_DONE ) {
641  mrsqlite3_log_error(ths, "Cannot commit transaction.");
642  }
643  }
644 
645  ths->m_transactionCount--;
646  }
647 }
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrstock_8c_source.html b/docs/user/html/mrstock_8c_source.html deleted file mode 100644 index f40446c7..00000000 --- a/docs/user/html/mrstock_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrstock.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrstock.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 /* Add translated strings that are used by the messager backend.
24 As the logging functions may use these strings, do not log any
25 errors from here. */
26 
27 
28 #include "mrmailbox_internal.h"
29 
30 
31 /*******************************************************************************
32  * Main interface
33  ******************************************************************************/
34 
35 
36 mrmailbox_t* s_localize_mb_obj = NULL;
37 
38 
39 static char* default_string(int id, int qty)
40 {
41  switch( id ) {
42  case MR_STR_NOMESSAGES: return safe_strdup("No messages.");
43  case MR_STR_SELF: return safe_strdup("Me");
44  case MR_STR_DRAFT: return safe_strdup("Draft");
45  case MR_STR_MEMBER: return mr_mprintf("%i member(s)", qty);
46  case MR_STR_CONTACT: return mr_mprintf("%i contact(s)", qty);
47  case MR_STR_VOICEMESSAGE: return safe_strdup("Voice message");
48  case MR_STR_DEADDROP: return safe_strdup("Mailbox");
49  case MR_STR_IMAGE: return safe_strdup("Image");
50  case MR_STR_GIF: return safe_strdup("GIF");
51  case MR_STR_VIDEO: return safe_strdup("Video");
52  case MR_STR_AUDIO: return safe_strdup("Audio");
53  case MR_STR_FILE: return safe_strdup("File");
54  case MR_STR_ENCRYPTEDMSG: return safe_strdup("Encrypted message");
55  case MR_STR_STATUSLINE: return safe_strdup("Sent with my Delta Chat Messenger");
56  case MR_STR_NEWGROUPDRAFT: return safe_strdup("Hello, I've just created the group \"%1$s\" for us.");
57  case MR_STR_MSGGRPNAME: return safe_strdup("Group name changed from \"%1$s\" to \"%2$s\".");
58  case MR_STR_MSGGRPIMGCHANGED: return safe_strdup("Group image changed.");
59  case MR_STR_MSGADDMEMBER: return safe_strdup("Member %1$s added.");
60  case MR_STR_MSGDELMEMBER: return safe_strdup("Member %1$s removed.");
61  case MR_STR_MSGGROUPLEFT: return safe_strdup("Group left.");
62  case MR_STR_ERROR: return safe_strdup("Error: %1$s");
63  case MR_STR_SELFNOTINGRP: return safe_strdup("You must be a member of the group to perform this action.");
64  case MR_STR_NONETWORK: return safe_strdup("No network available.");
65  case MR_STR_ENCR_E2E: return safe_strdup("End-to-end encryption enabled.");
66  case MR_STR_ENCR_TRANSP: return safe_strdup("Transport-encryption.");
67  case MR_STR_ENCR_NONE: return safe_strdup("No encryption.");
68  case MR_STR_FINGERPRINTS: return safe_strdup("Fingerprints");
69  case MR_STR_READRCPT: return safe_strdup("Return receipt");
70  case MR_STR_READRCPT_MAILBODY: return safe_strdup("This is a return receipt for the message \"%1$s\".");
71  case MR_STR_MSGGRPIMGDELETED: return safe_strdup("Group image deleted.");
72  case MR_STR_E2E_FINE: return safe_strdup("Please check, if all fingerprints match.");
73  case MR_STR_E2E_NO_AUTOCRYPT: return safe_strdup("E2EE will be enabled automatically.");
74  case MR_STR_E2E_DIS_BY_YOU: return safe_strdup("E2EE will be endable if you enable the corresponding option.");
75  case MR_STR_E2E_DIS_BY_RCPT: return safe_strdup("E2EE will be enabled if the recipients enables the corresponding option.");/* do not say, the recipient has _disabled_ the option, this may not be true! */
76  case MR_STR_ARCHIVEDCHATS: return safe_strdup("Archived chats");
77  case MR_STR_STARREDMSGS: return safe_strdup("Starred messages");
78  }
79  return safe_strdup("ErrStr");
80 }
81 
82 
83 char* mrstock_str(int id) /* get the string with the given ID, the result must be free()'d! */
84 {
85  char* ret = NULL;
86  if( s_localize_mb_obj && s_localize_mb_obj->m_cb ) {
87  ret = (char*)s_localize_mb_obj->m_cb(s_localize_mb_obj, MR_EVENT_GET_STRING, id, 0);
88  }
89  if( ret == NULL ) {
90  ret = default_string(id, 0);
91  }
92  return ret;
93 }
94 
95 
96 char* mrstock_str_repl_string(int id, const char* to_insert)
97 {
98  char* p1 = mrstock_str(id);
99  mr_str_replace(&p1, "%1$s", to_insert);
100  return p1;
101 }
102 
103 
104 char* mrstock_str_repl_int(int id, int to_insert_int)
105 {
106  char* ret, *to_insert_str = mr_mprintf("%i", (int)to_insert_int);
107  ret = mrstock_str_repl_string(id, to_insert_str);
108  free(to_insert_str);
109  return ret;
110 }
111 
112 
113 char* mrstock_str_repl_string2(int id, const char* to_insert, const char* to_insert2)
114 {
115  char* p1 = mrstock_str(id);
116  mr_str_replace(&p1, "%1$s", to_insert);
117  mr_str_replace(&p1, "%2$s", to_insert2);
118  return p1;
119 }
120 
121 
122 char* mrstock_str_repl_pl(int id, int cnt)
123 {
124  char* ret = NULL;
125  if( s_localize_mb_obj && s_localize_mb_obj->m_cb ) {
126  ret = (char*)s_localize_mb_obj->m_cb(s_localize_mb_obj, MR_EVENT_GET_QUANTITY_STRING, id, cnt);
127  }
128  if( ret == NULL ) {
129  ret = default_string(id, cnt);
130  }
131  return ret;
132 }
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - - diff --git a/docs/user/html/mrtools_8c_source.html b/docs/user/html/mrtools_8c_source.html deleted file mode 100644 index 2d2da63f..00000000 --- a/docs/user/html/mrtools_8c_source.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -Delta Chat Core C-Library: /home/bpetersen/projects/deltachat-core/src/mrtools.c Source File - - - - - - - - - - - -
-
- - - - - - -
-
Delta Chat Core C-Library -
-
-
- - - - - - -
-
- - -
- -
- - -
-
-
-
mrtools.c
-
-
-
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <stdarg.h>
24 #include <ctype.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/types.h> /* for getpid() */
29 #include <unistd.h> /* for getpid() */
30 #include <libetpan/libetpan.h>
31 #include <libetpan/mailimap_types.h>
32 #include "mrmailbox_internal.h"
33 
34 
35 /*******************************************************************************
36  * Math tools
37  ******************************************************************************/
38 
39 
40 int mr_exactly_one_bit_set(int v)
41 {
42  return (v && !(v & (v - 1))); /* via http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 */
43 }
44 
45 
46 /*******************************************************************************
47  * String tools
48  ******************************************************************************/
49 
50 
51 char* safe_strdup(const char* s) /* strdup(NULL) is undefined, save_strdup(NULL) returns an empty string in this case */
52 {
53  char* ret;
54  if( s ) {
55  if( (ret=strdup(s)) == NULL ) {
56  exit(16); /* cannot allocate (little) memory, unrecoverable error */
57  }
58  }
59  else {
60  if( (ret=(char*)calloc(1, 1)) == NULL ) {
61  exit(17); /* cannot allocate little memory, unrecoverable error */
62  }
63  }
64  return ret;
65 }
66 
67 
68 char* strdup_keep_null(const char* s) /* strdup(NULL) is undefined, safe_strdup_keep_null(NULL) returns NULL in this case */
69 {
70  return s? safe_strdup(s) : NULL;
71 }
72 
73 
74 int atoi_null_is_0(const char* s)
75 {
76  return s? atoi(s) : 0;
77 }
78 
79 
80 void mr_ltrim(char* buf)
81 {
82  size_t len;
83  const unsigned char* cur;
84 
85  if( buf && *buf ) {
86  len = strlen(buf);
87  cur = (const unsigned char*)buf;
88 
89  while( *cur && isspace(*cur) ) {
90  cur++; len--;
91  }
92 
93  if( (const unsigned char*)buf != cur ) {
94  memmove(buf, cur, len + 1);
95  }
96  }
97 }
98 
99 
100 void mr_rtrim(char* buf)
101 {
102  size_t len;
103  unsigned char* cur;
104 
105  if( buf && *buf ) {
106  len = strlen(buf);
107  cur = (unsigned char*)buf + len - 1;
108 
109  while( cur != (unsigned char*)buf && isspace(*cur) ) {
110  --cur, --len;
111  }
112 
113  cur[isspace(*cur) ? 0 : 1] = '\0';
114  }
115 }
116 
117 
118 void mr_trim(char* buf)
119 {
120  mr_ltrim(buf);
121  mr_rtrim(buf);
122 }
123 
124 
125 void mr_strlower_in_place(char* in)
126 {
127  char* p = in;
128  for ( ; *p; p++) {
129  *p = tolower(*p);
130  }
131 }
132 
133 
134 char* mr_strlower(const char* in) /* the result must be free()'d */
135 {
136  char* out = safe_strdup(in);
137 
138  char* p = out;
139  for ( ; *p; p++) {
140  *p = tolower(*p);
141  }
142 
143  return out;
144 }
145 
146 
147 int mr_str_replace(char** haystack, const char* needle, const char* replacement)
148 {
149  int replacements = 0, start_search_pos = 0, needle_len, replacement_len;
150 
151  if( haystack==NULL || *haystack==NULL || needle == NULL || needle[0]==0 ) {
152  return 0;
153  }
154 
155  needle_len = strlen(needle);
156  replacement_len = replacement? strlen(replacement) : 0;
157  while( 1 )
158  {
159  char* p2 = strstr((*haystack)+start_search_pos, needle);
160  if( p2==NULL ) { break; }
161  start_search_pos = (p2-(*haystack))+replacement_len; /* avoid recursion and skip the replaced part */
162 
163  *p2 = 0;
164  p2 += needle_len;
165  char* new_string = mr_mprintf("%s%s%s", *haystack, replacement? replacement : "", p2);
166  free(*haystack);
167  *haystack = new_string;
168  replacements++;
169  }
170 
171  return replacements;
172 }
173 
174 
175 char* mr_null_terminate(const char* in, int bytes) /* the result must be free()'d */
176 {
177  char* out = malloc(bytes+1);
178  if( out==NULL ) {
179  exit(45);
180  }
181 
182  if( in && bytes > 0 ) {
183  strncpy(out, in, bytes);
184  }
185  out[bytes] = 0;
186  return out;
187 }
188 
189 
190 char* mr_mprintf(const char* format, ...)
191 {
192  char testbuf[1];
193  char* buf;
194  int char_cnt_without_zero;
195 
196  va_list argp;
197  va_list argp_copy;
198  va_start(argp, format);
199  va_copy(argp_copy, argp);
200 
201  char_cnt_without_zero = vsnprintf(testbuf, 0, format, argp);
202  va_end(argp);
203  if( char_cnt_without_zero < 0) {
204  va_end(argp_copy);
205  return safe_strdup("ErrFmt");
206  }
207 
208  buf = malloc(char_cnt_without_zero+2 /* +1 would be enough, however, protect against off-by-one-errors */);
209  if( buf == NULL ) {
210  va_end(argp_copy);
211  return safe_strdup("ErrMem");
212  }
213 
214  vsnprintf(buf, char_cnt_without_zero+1, format, argp_copy);
215  va_end(argp_copy);
216  return buf;
217 
218  #if 0 /* old implementation based upon sqlite3 */
219  char *sqlite_str, *c_string;
220 
221  va_list argp;
222  va_start(argp, format); /* expects the last non-variable argument as the second parameter */
223  sqlite_str = sqlite3_vmprintf(format, argp);
224  va_end(argp);
225 
226  if( sqlite_str == NULL ) {
227  return safe_strdup("ErrFmt"); /* error - the result must be free()'d */
228  }
229 
230  /* as sqlite-strings must be freed using sqlite3_free() instead of a simple free(), convert it to a normal c-string */
231  c_string = safe_strdup(sqlite_str); /* exists on errors */
232  sqlite3_free(sqlite_str);
233  return c_string; /* success - the result must be free()'d */
234  #endif /* /old implementation based upon sqlite3 */
235 }
236 
237 
238 void mr_remove_cr_chars(char* buf)
239 {
240  /* remove all carriage return characters (`\r`) from the null-terminated buffer;
241  the buffer itself is modified for this purpose */
242 
243  const char* p1 = buf; /* search for first `\r` */
244  while( *p1 ) {
245  if( *p1 == '\r' ) {
246  break;
247  }
248  p1++;
249  }
250 
251  char* p2 = (char*)p1; /* p1 is `\r` or null-byte; start removing `\r` */
252  while( *p1 ) {
253  if( *p1 != '\r' ) {
254  *p2 = *p1;
255  p2++;
256  }
257  p1++;
258  }
259 
260  /* add trailing null-byte */
261  *p2 = 0;
262 }
263 
264 
265 void mr_replace_bad_utf8_chars(char* buf)
266 {
267  if( buf==NULL ) {
268  return;
269  }
270 
271  unsigned char* p1 = (unsigned char*)buf; /* force unsigned - otherwise the `> ' '` comparison will fail */
272  int p1len = strlen(buf);
273  int c, i, ix, n, j;
274  for( i=0, ix=p1len; i < ix; i++ )
275  {
276  c = p1[i];
277  if( c > 0 && c <= 0x7f ) { n=0; } /* 0bbbbbbb */
278  else if( (c & 0xE0) == 0xC0 ) { n=1; } /* 110bbbbb */
279  else if( c==0xed && i<(ix-1) && (p1[i+1] & 0xa0)==0xa0) { goto error; } /* U+d800 to U+dfff */
280  else if( (c & 0xF0) == 0xE0 ) { n=2; } /* 1110bbbb */
281  else if( (c & 0xF8) == 0xF0) { n=3; } /* 11110bbb */
282  //else if( (c & 0xFC) == 0xF8) { n=4; } /* 111110bb - not valid in https://tools.ietf.org/html/rfc3629 */
283  //else if( (c & 0xFE) == 0xFC) { n=5; } /* 1111110b - not valid in https://tools.ietf.org/html/rfc3629 */
284  else { goto error; }
285 
286  for( j = 0; j < n && i < ix; j++ ) { /* n bytes matching 10bbbbbb follow ? */
287  if( (++i == ix) || (( p1[i] & 0xC0) != 0x80) ) {
288  goto error;
289  }
290  }
291  }
292 
293  /* everything is fine */
294  return;
295 
296 error:
297  /* there are errors in the string -> replace potential errors by the character `_`
298  (to avoid problems in filenames, we do not use eg. `?`) */
299  while( *p1 ) {
300  if( *p1 > 0x7f ) {
301  *p1 = '_';
302  }
303  p1++;
304  }
305 }
306 
307 
308 #if 0 /* not needed at the moment */
309 static size_t mr_utf8_strlen(const char* s)
310 {
311  size_t i = 0, j = 0;
312  while( s[i] ) {
313  if( (s[i]&0xC0) != 0x80 )
314  j++;
315  i++;
316  }
317  return j;
318 }
319 #endif
320 
321 
322 static size_t mr_utf8_strnlen(const char* s, size_t n)
323 {
324  size_t i = 0, j = 0;
325  while( i < n ) {
326  if( (s[i]&0xC0) != 0x80 )
327  j++;
328  i++;
329  }
330  return j;
331 }
332 
333 
334 void mr_truncate_n_unwrap_str(char* buf, int approx_characters, int do_unwrap)
335 {
336  /* Function unwraps the given string and removes unnecessary whitespace.
337  Function stops processing after approx_characters are processed.
338  (as we're using UTF-8, for simplicity, we cut the string only at whitespaces). */
339  int lastIsCharacter = 0;
340  unsigned char* p1 = (unsigned char*)buf; /* force unsigned - otherwise the `> ' '` comparison will fail */
341  while( *p1 ) {
342  if( *p1 > ' ' ) {
343  lastIsCharacter = 1;
344  }
345  else {
346  if( lastIsCharacter ) {
347  size_t used_bytes = (size_t)((uintptr_t)p1 - (uintptr_t)buf);
348  if( mr_utf8_strnlen(buf, used_bytes) >= approx_characters ) {
349  const char* ellipse_utf8 = " ...";
350  size_t buf_bytes = strlen(buf);
351  if( buf_bytes-used_bytes >= strlen(ellipse_utf8) /* check if we have room for the ellipse */ ) {
352  strcpy((char*)p1, ellipse_utf8);
353  }
354  break;
355  }
356  lastIsCharacter = 0;
357  if( do_unwrap ) {
358  *p1 = ' ';
359  }
360  }
361  else {
362  if( do_unwrap ) {
363  *p1 = '\r'; /* removed below */
364  }
365  }
366  }
367 
368  p1++;
369  }
370 
371  if( do_unwrap ) {
372  mr_remove_cr_chars(buf);
373  }
374 }
375 
376 
377 carray* mr_split_into_lines(const char* buf_terminated)
378 {
379  carray* lines = carray_new(1024);
380 
381  size_t line_chars = 0;
382  const char* p1 = buf_terminated;
383  const char* line_start = p1;
384  unsigned int l_indx;
385  while( *p1 ) {
386  if( *p1 == '\n' ) {
387  carray_add(lines, (void*)strndup(line_start, line_chars), &l_indx);
388  p1++;
389  line_start = p1;
390  line_chars = 0;
391  }
392  else {
393  p1++;
394  line_chars++;
395  }
396  }
397  carray_add(lines, (void*)strndup(line_start, line_chars), &l_indx);
398 
399  return lines; /* should be freed using mr_free_splitted_lines() */
400 }
401 
402 
403 void mr_free_splitted_lines(carray* lines)
404 {
405  if( lines ) {
406  int i, cnt = carray_count(lines);
407  for( i = 0; i < cnt; i++ ) {
408  free(carray_get(lines, i));
409  }
410  carray_free(lines);
411  }
412 }
413 
414 
415 char* mr_insert_breaks(const char* in, int break_every, const char* break_chars)
416 {
417  /* insert a space every n characters, the return must be free()'d.
418  this is useful for allow lines being wrapped according to RFC 5322 (adds linebreaks before spaces) */
419 
420  if( in == NULL || break_every <= 0 || break_chars == NULL ) {
421  return safe_strdup(in);
422  }
423 
424  int out_len = strlen(in), chars_added = 0;
425  int break_chars_len = strlen(break_chars);
426  out_len += (out_len/break_every+1)*break_chars_len + 1/*nullbyte*/;
427 
428  char* out = malloc(out_len);
429  if( out == NULL ) { return NULL; }
430 
431  const char* i = in;
432  char* o = out;
433  while( *i ) {
434  *o++ = *i++;
435  chars_added++;
436  if( chars_added==break_every && *i ) {
437  strcpy(o, break_chars);
438  o+=break_chars_len;
439  chars_added = 0;
440  }
441  }
442  *o = 0;
443  return out;
444 }
445 
446 
447 char* mr_arr_to_string(const uint32_t* arr, int cnt)
448 {
449  /* return comma-separated value-string from integer array */
450  if( arr==NULL || cnt <= 0 ) {
451  return safe_strdup("");
452  }
453 
454  char* ret = malloc(cnt*12/*sign,10 digits,comma*/+1/*terminating zero*/);
455  if( ret == NULL ) { exit(35); }
456  ret[0] = 0;
457 
458  int i;
459  for( i=0; i<cnt; i++ ) {
460  if( i ) {
461  strcat(ret, ",");
462  }
463  sprintf(&ret[strlen(ret)], "%lu", (unsigned long)arr[i]);
464  }
465 
466  return ret;
467 }
468 
469 
470 /*******************************************************************************
471  * String tools - mrstrbuilder_t
472  ******************************************************************************/
473 
474 
475 void mrstrbuilder_init(mrstrbuilder_t* ths)
476 {
477  if( ths==NULL ) {
478  return;
479  }
480 
481  ths->m_allocated = 128; /* do not get too large here, esp. if use _many_ of these objects at the same time (currently, this is not the case) */
482  ths->m_buf = malloc(ths->m_allocated); if( ths->m_buf==NULL ) { exit(38); }
483  ths->m_buf[0] = 0;
484  ths->m_free = ths->m_allocated - 1 /*the nullbyte! */;
485  ths->m_eos = ths->m_buf;
486 }
487 
488 
489 char* mrstrbuilder_cat(mrstrbuilder_t* ths, const char* text)
490 {
491  /* this function MUST NOT call logging functions as it is used to output the log */
492  if( ths==NULL || text==NULL ) {
493  return NULL;
494  }
495 
496  int len = strlen(text);
497 
498  if( len > ths->m_free ) {
499  int add_bytes = MR_MAX(len, ths->m_allocated);
500  int old_offset = (int)(ths->m_eos - ths->m_buf);
501 
502  ths->m_allocated = ths->m_allocated + add_bytes;
503  ths->m_buf = realloc(ths->m_buf, ths->m_allocated+add_bytes); if( ths->m_buf==NULL ) { exit(39); }
504  ths->m_free = ths->m_free + add_bytes;
505  ths->m_eos = ths->m_buf + old_offset;
506  }
507 
508  char* ret = ths->m_eos;
509 
510  strcpy(ths->m_eos, text);
511  ths->m_eos += len;
512  ths->m_free -= len;
513 
514  return ret;
515 }
516 
517 
518 void mrstrbuilder_empty(mrstrbuilder_t* ths)
519 {
520  /* set the string to a length of 0, does not free the buffer */
521  ths->m_buf[0] = 0;
522  ths->m_free = ths->m_allocated - 1 /*the nullbyte! */;
523  ths->m_eos = ths->m_buf;
524 }
525 
526 
527 /*******************************************************************************
528  * Decode header strings
529  ******************************************************************************/
530 
531 
532 char* mr_decode_header_string(const char* in)
533 {
534  /* decode strings as. `=?UTF-8?Q?Bj=c3=b6rn_Petersen?=`)
535  if `in` is NULL, `out` is NULL as well; also returns NULL on errors */
536 
537  if( in == NULL ) {
538  return NULL; /* no string given */
539  }
540 
541  #define DEF_INCOMING_CHARSET "iso-8859-1"
542  #define DEF_DISPLAY_CHARSET "utf-8"
543  char* out = NULL;
544  size_t cur_token = 0;
545  int r = mailmime_encoded_phrase_parse(DEF_INCOMING_CHARSET, in, strlen(in), &cur_token, DEF_DISPLAY_CHARSET, &out);
546  if( r != MAILIMF_NO_ERROR || out == NULL ) {
547  out = safe_strdup(in); /* error, make a copy of the original string (as we free it later) */
548  }
549 
550  return out; /* must be free()'d by the caller */
551 }
552 
553 
554 /*******************************************************************************
555  * Encode header strings, code inspired by etpan-ng
556  ******************************************************************************/
557 
558 
559 #define ERROR_MEMORY MAILIMAP_ERROR_MEMORY
560 #define NO_ERROR MAILIMAP_NO_ERROR
561 #define MAX_IMF_LINE 666 /* we do not fold at position 72; this would result in empty words as `=?utf-8?Q??=` which are correct, but cannot be displayed by some mail programs (eg. Android Stock Mail)
562  however, this is not needed, as long as _one_ word is not longer than 72 characters. _if_ it is, the display may get weired. This affects the subject only.
563  the best solution wor all this would be if libetpan encodes the line as only libetpan knowns when a header line is full */
564 
565 static inline int to_be_quoted(const char * word, size_t size)
566 {
567  int do_quote;
568  const char * cur;
569  size_t i;
570 
571  do_quote = 0;
572  cur = word;
573  for(i = 0 ; i < size ; i ++) {
574  switch (* cur) {
575  case ',':
576  case ':':
577  case '!':
578  case '"':
579  case '#':
580  case '$':
581  case '@':
582  case '[':
583  case '\\':
584  case ']':
585  case '^':
586  case '`':
587  case '{':
588  case '|':
589  case '}':
590  case '~':
591  case '=':
592  case '?':
593  case '_':
594  do_quote = 1;
595  break;
596  default:
597  if (((unsigned char) * cur) >= 128)
598  do_quote = 1;
599  break;
600  }
601  cur ++;
602  }
603 
604  return do_quote;
605 }
606 
607 static int quote_word(const char * display_charset,
608  MMAPString * mmapstr, const char * word, size_t size)
609 {
610  const char * cur;
611  size_t i;
612  char hex[4];
613  int col;
614 
615  if (mmap_string_append(mmapstr, "=?") == NULL)
616  return ERROR_MEMORY;
617  if (mmap_string_append(mmapstr, display_charset) == NULL)
618  return ERROR_MEMORY;
619  if (mmap_string_append(mmapstr, "?Q?") == NULL)
620  return ERROR_MEMORY;
621 
622  col = mmapstr->len;
623 
624  cur = word;
625  for(i = 0 ; i < size ; i ++) {
626  int do_quote_char;
627 
628  #if MAX_IMF_LINE != 666
629  if (col + 2 /* size of "?=" */
630  + 3 /* max size of newly added character */
631  + 1 /* minimum column of string in a
632  folded header */ >= MAX_IMF_LINE) {
633  int old_pos;
634  /* adds a concatened encoded word */
635 
636  if (mmap_string_append(mmapstr, "?=") == NULL)
637  return ERROR_MEMORY;
638 
639  if (mmap_string_append(mmapstr, " ") == NULL)
640  return ERROR_MEMORY;
641 
642  old_pos = mmapstr->len;
643 
644  if (mmap_string_append(mmapstr, "=?") == NULL)
645  return ERROR_MEMORY;
646  if (mmap_string_append(mmapstr, display_charset) == NULL)
647  return ERROR_MEMORY;
648  if (mmap_string_append(mmapstr, "?Q?") == NULL)
649  return ERROR_MEMORY;
650 
651  col = mmapstr->len - old_pos;
652  }
653  #endif
654 
655  do_quote_char = 0;
656  switch (* cur) {
657  case ',':
658  case ':':
659  case '!':
660  case '"':
661  case '#':
662  case '$':
663  case '@':
664  case '[':
665  case '\\':
666  case ']':
667  case '^':
668  case '`':
669  case '{':
670  case '|':
671  case '}':
672  case '~':
673  case '=':
674  case '?':
675  case '_':
676  do_quote_char = 1;
677  break;
678 
679  default:
680  if (((unsigned char) * cur) >= 128)
681  do_quote_char = 1;
682  break;
683  }
684 
685  if (do_quote_char) {
686  snprintf(hex, 4, "=%2.2X", (unsigned char) * cur);
687  if (mmap_string_append(mmapstr, hex) == NULL)
688  return ERROR_MEMORY;
689  col += 3;
690  }
691  else {
692  if (* cur == ' ') {
693  if (mmap_string_append_c(mmapstr, '_') == NULL)
694  return ERROR_MEMORY;
695  }
696  else {
697  if (mmap_string_append_c(mmapstr, * cur) == NULL)
698  return ERROR_MEMORY;
699  }
700  col += 3;
701  }
702  cur ++;
703  }
704 
705  if (mmap_string_append(mmapstr, "?=") == NULL)
706  return ERROR_MEMORY;
707 
708  return 0;
709 }
710 
711 static void get_word(const char * begin, const char ** pend, int * pto_be_quoted)
712 {
713  const char * cur;
714 
715  cur = begin;
716 
717  while ((* cur != ' ') && (* cur != '\t') && (* cur != '\0')) {
718  cur ++;
719  }
720 
721  #if MAX_IMF_LINE != 666
722  if (cur - begin +
723  1 /* minimum column of string in a
724  folded header */ > MAX_IMF_LINE)
725  * pto_be_quoted = 1;
726  else
727  #endif
728  * pto_be_quoted = to_be_quoted(begin, cur - begin);
729 
730  * pend = cur;
731 }
732 
733 char* mr_encode_header_string(const char* phrase)
734 {
735  char * str;
736  const char * cur;
737  MMAPString * mmapstr;
738 
739  mmapstr = mmap_string_new("");
740  if (mmapstr == NULL)
741  goto err;
742 
743  cur = phrase;
744  while (* cur != '\0') {
745  const char * begin;
746  const char * end;
747  int r;
748  int do_quote;
749  int quote_words;
750 
751  begin = cur;
752  end = begin;
753  quote_words = 0;
754  do_quote = 1;
755 
756  while (* cur != '\0') {
757  get_word(cur, &cur, &do_quote);
758  if (do_quote) {
759  quote_words = 1;
760  end = cur;
761  }
762  else
763  break;
764  if (* cur != '\0')
765  cur ++;
766  }
767 
768  if (quote_words) {
769  r = quote_word(DEF_DISPLAY_CHARSET, mmapstr, begin, end - begin);
770  if (r != NO_ERROR)
771  goto free_mmap;
772 
773  if ((* end == ' ') || (* end == '\t')) {
774  if (mmap_string_append_c(mmapstr, * end) == 0)
775  goto free_mmap;
776  end ++;
777  }
778 
779  if (* end != '\0') {
780  if (mmap_string_append_len(mmapstr, end, cur - end) == NULL)
781  goto free_mmap;
782  }
783  }
784  else {
785  if (mmap_string_append_len(mmapstr, begin, cur - begin) == NULL)
786  goto free_mmap;
787  }
788 
789  if ((* cur == ' ') || (* cur == '\t')) {
790  if (mmap_string_append_c(mmapstr, * cur) == 0)
791  goto free_mmap;
792  cur ++;
793  }
794  }
795 
796  str = strdup(mmapstr->str);
797  if (str == NULL)
798  goto free_mmap;
799 
800  mmap_string_free(mmapstr);
801 
802  return str;
803 
804  free_mmap:
805  mmap_string_free(mmapstr);
806  err:
807  return NULL;
808  #if 0
809  size_t in_len = strlen(in);
810  int col = 0;
811  MMAPString* quoted_printable = mmap_string_new("");
812 
813  mailmime_quoted_printable_write_mem(quoted_printable, &col, true, in, in_len);
814 
815  if( quoted_printable->len<=in_len || quoted_printable->str==NULL
816  || quoted_printable->len>=72-9 ) { /* 72-9=MAX_MAIL_COL-strlen("Subject: ") -- we do not encode as libetpan does not fold the lines correctly (would expect = at the end of the line) */
817  mmap_string_free(quoted_printable);
818  return safe_strdup(in);
819  }
820  else {
821  char* encoded = mr_mprintf("=?UTF-8?Q?%s?=", quoted_printable->str);
822  mmap_string_free(quoted_printable);
823  return encoded;
824  }
825  #else
826  #endif
827 }
828 
829 
830 /* ===================================================================
831  * UTF-7 conversion routines as in RFC 2192
832  * ===================================================================
833  * These two functions from:
834  * libimap library.
835  * Copyright (C) 2003-2004 Pawel Salek. */
836 
837 /* UTF7 modified base64 alphabet */
838 static char base64chars[] =
839  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
840 #define UNDEFINED 64
841 
842 /* UTF16 definitions */
843 #define UTF16MASK 0x03FFUL
844 #define UTF16SHIFT 10
845 #define UTF16BASE 0x10000UL
846 #define UTF16HIGHSTART 0xD800UL
847 #define UTF16HIGHEND 0xDBFFUL
848 #define UTF16LOSTART 0xDC00UL
849 #define UTF16LOEND 0xDFFFUL
850 
851 
852  /* Convert an IMAP mailbox to a UTF-8 string.
853  * dst needs to have roughly 4 times the storage space of src
854  * Hex encoding can triple the size of the input
855  * UTF-7 can be slightly denser than UTF-8
856  * (worst case: 8 octets UTF-7 becomes 9 octets UTF-8)
857  */
858 char* imap_modified_utf7_to_utf8(const char *mbox, int change_spaces)
859 {
860  unsigned c, i, bitcount;
861  unsigned long ucs4, utf16, bitbuf;
862  unsigned char base64[256];
863  const char *src;
864  char *dst, *res = (char*)malloc(2*strlen(mbox)+1);
865 
866  dst = res;
867  src = mbox;
868  if(!dst) return NULL;
869  /* initialize modified base64 decoding table */
870  memset(base64, UNDEFINED, sizeof (base64));
871  for (i = 0; i < sizeof (base64chars); ++i) {
872  base64[(unsigned)base64chars[i]] = i;
873  }
874 
875  /* loop until end of string */
876  while (*src != '\0') {
877  c = *src++;
878  /* deal with literal characters and &- */
879  if (c != '&' || *src == '-') {
880  /* encode literally */
881  if (change_spaces && c == '_')
882  *dst++ = ' ';
883  else
884  *dst++ = c;
885  /* skip over the '-' if this is an &- sequence */
886  if (c == '&') ++src;
887  } else {
888  /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
889  bitbuf = 0;
890  bitcount = 0;
891  ucs4 = 0;
892  while ((c = base64[(unsigned char) *src]) != UNDEFINED) {
893  ++src;
894  bitbuf = (bitbuf << 6) | c;
895  bitcount += 6;
896  /* enough bits for a UTF-16 character? */
897  if (bitcount >= 16) {
898  bitcount -= 16;
899  utf16 = (bitcount ? bitbuf >> bitcount
900  : bitbuf) & 0xffff;
901  /* convert UTF16 to UCS4 */
902  if
903  (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) {
904  ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
905  continue;
906  } else if
907  (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) {
908  ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
909  } else {
910  ucs4 = utf16;
911  }
912 
913  /* convert UTF-16 range of UCS4 to UTF-8 */
914  if (ucs4 <= 0x7fUL) {
915  dst[0] = ucs4;
916  dst += 1;
917  } else if (ucs4 <= 0x7ffUL) {
918  dst[0] = 0xc0 | (ucs4 >> 6);
919  dst[1] = 0x80 | (ucs4 & 0x3f);
920  dst += 2;
921  } else if (ucs4 <= 0xffffUL) {
922  dst[0] = 0xe0 | (ucs4 >> 12);
923  dst[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
924  dst[2] = 0x80 | (ucs4 & 0x3f);
925  dst += 3;
926  } else {
927  dst[0] = 0xf0 | (ucs4 >> 18);
928  dst[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
929  dst[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
930  dst[3] = 0x80 | (ucs4 & 0x3f);
931  dst += 4;
932  }
933  }
934  }
935  /* skip over trailing '-' in modified UTF-7 encoding */
936  if (*src == '-') ++src;
937  }
938  }
939  /* terminate destination string */
940  *dst = '\0';
941  return res;
942 }
943 
944 /* Convert hex coded UTF-8 string to modified UTF-7 IMAP mailbox
945  * dst should be about twice the length of src to deal with non-hex
946  * coded URLs
947  */
948 char* imap_utf8_to_modified_utf7(const char *src, int change_spaces)
949 {
950  unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
951  unsigned long ucs4 = 0, bitbuf = 0;
952 
953  /* initialize hex lookup table */
954  char *dst, *res;
955 
956  if (!src) return NULL;
957 
958  res = (char*)malloc(2*strlen(src)+1);
959  dst = res;
960  if(!dst) return NULL;
961 
962  utf7mode = 0;
963  utf8total = 0;
964  bitstogo = 0;
965  utf8pos = 0;
966  while ((c = (unsigned char)*src) != '\0') {
967  ++src;
968  /* normal character? */
969  if (c >= ' ' && c <= '~' && (c != '_' || !change_spaces)) {
970  /* switch out of UTF-7 mode */
971  if (utf7mode) {
972  if (bitstogo) {
973  *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
974  }
975  *dst++ = '-';
976  utf7mode = 0;
977  utf8pos = 0;
978  bitstogo = 0;
979  utf8total= 0;
980  }
981  if (change_spaces && c == ' ')
982  *dst++ = '_';
983  else
984  *dst++ = c;
985  /* encode '&' as '&-' */
986  if (c == '&') {
987  *dst++ = '-';
988  }
989  continue;
990  }
991  /* switch to UTF-7 mode */
992  if (!utf7mode) {
993  *dst++ = '&';
994  utf7mode = 1;
995  }
996  /* Encode US-ASCII characters as themselves */
997  if (c < 0x80) {
998  ucs4 = c;
999  } else if (utf8total) {
1000  /* save UTF8 bits into UCS4 */
1001  ucs4 = (ucs4 << 6) | (c & 0x3FUL);
1002  if (++utf8pos < utf8total) {
1003  continue;
1004  }
1005  } else {
1006  utf8pos = 1;
1007  if (c < 0xE0) {
1008  utf8total = 2;
1009  ucs4 = c & 0x1F;
1010  } else if (c < 0xF0) {
1011  utf8total = 3;
1012  ucs4 = c & 0x0F;
1013  } else {
1014  /* NOTE: can't convert UTF8 sequences longer than 4 */
1015  utf8total = 4;
1016  ucs4 = c & 0x03;
1017  }
1018  continue;
1019  }
1020  /* loop to split ucs4 into two utf16 chars if necessary */
1021  utf8total = 0;
1022  do {
1023  if (ucs4 >= UTF16BASE) {
1024  ucs4 -= UTF16BASE;
1025  bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT)
1026  + UTF16HIGHSTART);
1027  ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
1028  utf16flag = 1;
1029  } else {
1030  bitbuf = (bitbuf << 16) | ucs4;
1031  utf16flag = 0;
1032  }
1033  bitstogo += 16;
1034  /* spew out base64 */
1035  while (bitstogo >= 6) {
1036  bitstogo -= 6;
1037  *dst++ = base64chars[(bitstogo ? (bitbuf >> bitstogo)
1038  : bitbuf)
1039  & 0x3F];
1040  }
1041  } while (utf16flag);
1042  }
1043  /* if in UTF-7 mode, finish in ASCII */
1044  if (utf7mode) {
1045  if (bitstogo) {
1046  *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
1047  }
1048  *dst++ = '-';
1049  }
1050  /* tie off string */
1051  *dst = '\0';
1052  return res;
1053 }
1054 
1055 
1056 /*******************************************************************************
1057  * URL encoding and decoding
1058  ******************************************************************************/
1059 
1060 
1061 /* Converts an integer value to its hex character*/
1062 char to_hex(char code) {
1063  static char hex[] = "0123456789abcdef";
1064  return hex[code & 15];
1065 }
1066 
1067 /* Returns a url-encoded version of str, be sure to free() the result. Inspired by http://www.geekhideout.com/urlcode.shtml */
1068 char* mr_url_encode(const char *str) {
1069  const char *pstr = str;
1070  char *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf;
1071  while (*pstr) {
1072  if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
1073  *pbuf++ = *pstr;
1074  else if (*pstr == ' ')
1075  *pbuf++ = '+';
1076  else
1077  *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
1078  pstr++;
1079  }
1080  *pbuf = '\0';
1081  return buf;
1082 }
1083 
1084 
1085 /* Converts a hex character to its integer value */
1086 /*static char from_hex(char ch) {
1087  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
1088 }*/
1089 
1090 
1091 /* Returns a url-decoded version of str, be sure to free() the returned string after use */
1092 /*char* mr_url_decode(const char *str) {
1093  const char *pstr = str;
1094  char *buf = malloc(strlen(str) + 1), *pbuf = buf;
1095  while (*pstr) {
1096  if (*pstr == '%') {
1097  if (pstr[1] && pstr[2]) {
1098  *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
1099  pstr += 2;
1100  }
1101  } else if (*pstr == '+') {
1102  *pbuf++ = ' ';
1103  } else {
1104  *pbuf++ = *pstr;
1105  }
1106  pstr++;
1107  }
1108  *pbuf = '\0';
1109  return buf;
1110 }*/
1111 
1112 
1113 /*******************************************************************************
1114  * carray/clist tools
1115  ******************************************************************************/
1116 
1117 
1118 int carray_search(carray* haystack, void* needle, unsigned int* indx)
1119 {
1120  void** data = carray_data(haystack);
1121  unsigned int i, cnt = carray_count(haystack);
1122  for( i=0; i<cnt; i++ )
1123  {
1124  if( data[i] == needle ) {
1125  if( indx ) {
1126  *indx = i;
1127  }
1128  return 1;
1129  }
1130  }
1131 
1132  return 0;
1133 }
1134 
1135 
1136 uint32_t carray_get_uint32(carray* haystack, unsigned int indx)
1137 {
1138  return (uint32_t)(uintptr_t)haystack->array[indx];
1139 }
1140 
1141 
1142 void clist_free_content(const clist* haystack)
1143 {
1144  clistiter* iter;
1145  for( iter=clist_begin(haystack); iter!=NULL; iter=clist_next(iter) ) {
1146  free(iter->data);
1147  iter->data = NULL;
1148  }
1149 }
1150 
1151 
1152 int clist_search_string_nocase(const clist* haystack, const char* needle)
1153 {
1154  clistiter* iter;
1155  for( iter=clist_begin(haystack); iter!=NULL; iter=clist_next(iter) ) {
1156  if( strcasecmp((const char*)iter->data, needle)==0 ) {
1157  return 1;
1158  }
1159  }
1160  return 0;
1161 }
1162 
1163 
1164 /*******************************************************************************
1165  * date/time tools
1166  ******************************************************************************/
1167 
1168 
1169 static int tmcomp(struct tm * atmp, struct tm * btmp) /* from mailcore2 */
1170 {
1171  int result;
1172 
1173  if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
1174  (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
1175  (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
1176  (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
1177  (result = (atmp->tm_min - btmp->tm_min)) == 0)
1178  result = atmp->tm_sec - btmp->tm_sec;
1179  return result;
1180 }
1181 
1182 
1183 static time_t mkgmtime(struct tm * tmp) /* from mailcore2 */
1184 {
1185  int dir;
1186  int bits;
1187  int saved_seconds;
1188  time_t t;
1189  struct tm yourtm, mytm;
1190 
1191  yourtm = *tmp;
1192  saved_seconds = yourtm.tm_sec;
1193  yourtm.tm_sec = 0;
1194  /*
1195  ** Calculate the number of magnitude bits in a time_t
1196  ** (this works regardless of whether time_t is
1197  ** signed or unsigned, though lint complains if unsigned).
1198  */
1199  for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
1200  ;
1201  /*
1202  ** If time_t is signed, then 0 is the median value,
1203  ** if time_t is unsigned, then 1 << bits is median.
1204  */
1205  if(bits > 40) bits = 40;
1206  t = (t < 0) ? 0 : ((time_t) 1 << bits);
1207  for ( ; ; ) {
1208  gmtime_r(&t, &mytm);
1209  dir = tmcomp(&mytm, &yourtm);
1210  if (dir != 0) {
1211  if (bits-- < 0) {
1212  return MR_INVALID_TIMESTAMP;
1213  }
1214  if (bits < 0)
1215  --t;
1216  else if (dir > 0)
1217  t -= (time_t) 1 << bits;
1218  else t += (time_t) 1 << bits;
1219  continue;
1220  }
1221  break;
1222  }
1223  t += saved_seconds;
1224  return t;
1225 }
1226 
1227 
1228 time_t mr_timestamp_from_date(struct mailimf_date_time * date_time) /* from mailcore2 */
1229 {
1230  struct tm tmval;
1231  time_t timeval;
1232  int zone_min;
1233  int zone_hour;
1234 
1235  tmval.tm_sec = date_time->dt_sec;
1236  tmval.tm_min = date_time->dt_min;
1237  tmval.tm_hour = date_time->dt_hour;
1238  tmval.tm_mday = date_time->dt_day;
1239  tmval.tm_mon = date_time->dt_month - 1;
1240  if (date_time->dt_year < 1000) {
1241  /* workaround when century is not given in year */
1242  tmval.tm_year = date_time->dt_year + 2000 - 1900;
1243  }
1244  else {
1245  tmval.tm_year = date_time->dt_year - 1900;
1246  }
1247 
1248  timeval = mkgmtime(&tmval);
1249 
1250  if (date_time->dt_zone >= 0) {
1251  zone_hour = date_time->dt_zone / 100;
1252  zone_min = date_time->dt_zone % 100;
1253  }
1254  else {
1255  zone_hour = -((- date_time->dt_zone) / 100);
1256  zone_min = -((- date_time->dt_zone) % 100);
1257  }
1258  timeval -= zone_hour * 3600 + zone_min * 60;
1259 
1260  return timeval;
1261 }
1262 
1263 
1264 long mr_gm2local_offset(void)
1265 {
1266  /* returns the offset that must be _added_ to an UTC/GMT-time to create the localtime.
1267  the function may return nagative values. */
1268  time_t gmtime = time(NULL);
1269  struct tm timeinfo = {0};
1270  localtime_r(&gmtime, &timeinfo);
1271  return timeinfo.tm_gmtoff;
1272 }
1273 
1274 
1275 char* mr_timestamp_to_str(time_t wanted)
1276 {
1277  struct tm wanted_struct;
1278  memcpy(&wanted_struct, localtime(&wanted), sizeof(struct tm));
1279 
1280  /* if you need the current time for relative dates, use the following lines:
1281  time_t curr;
1282  struct tm curr_struct;
1283  time(&curr);
1284  memcpy(&curr_struct, localtime(&curr), sizeof(struct tm));
1285  */
1286 
1287  return mr_mprintf("%02i.%02i.%04i %02i:%02i:%02i",
1288  (int)wanted_struct.tm_mday, (int)wanted_struct.tm_mon+1, (int)wanted_struct.tm_year+1900,
1289  (int)wanted_struct.tm_hour, (int)wanted_struct.tm_min, (int)wanted_struct.tm_sec);
1290 }
1291 
1292 
1293 struct mailimap_date_time* mr_timestamp_to_mailimap_date_time(time_t timeval)
1294 {
1295  struct tm gmt;
1296  struct tm lt;
1297  int off;
1298  struct mailimap_date_time * date_time;
1299  int sign;
1300  int hour;
1301  int min;
1302 
1303  gmtime_r(&timeval, &gmt);
1304  localtime_r(&timeval, &lt);
1305 
1306  off = (int) ((mkgmtime(&lt) - mkgmtime(&gmt)) / 60);
1307  if (off < 0) {
1308  sign = -1;
1309  }
1310  else {
1311  sign = 1;
1312  }
1313  off = off * sign;
1314  min = off % 60;
1315  hour = off / 60;
1316  off = hour * 100 + min;
1317  off = off * sign;
1318 
1319  date_time = mailimap_date_time_new(lt.tm_mday, lt.tm_mon + 1,
1320  lt.tm_year + 1900,
1321  lt.tm_hour, lt.tm_min, lt.tm_sec,
1322  off);
1323 
1324  return date_time;
1325 }
1326 
1327 
1328 /*******************************************************************************
1329  * Time smearing
1330  ******************************************************************************/
1331 
1332 
1333 static time_t s_last_smeared_timestamp = 0;
1334 #define MR_MAX_SECONDS_TO_LEND_FROM_FUTURE 5
1335 
1336 
1337 time_t mr_create_smeared_timestamp__(void)
1338 {
1339  time_t now = time(NULL);
1340  time_t ret = now;
1341  if( ret <= s_last_smeared_timestamp ) {
1342  ret = s_last_smeared_timestamp+1;
1343  if( (ret-now) > MR_MAX_SECONDS_TO_LEND_FROM_FUTURE ) {
1344  ret = now + MR_MAX_SECONDS_TO_LEND_FROM_FUTURE;
1345  }
1346  }
1347  s_last_smeared_timestamp = ret;
1348  return ret;
1349 }
1350 
1351 
1352 time_t mr_create_smeared_timestamps__(int count)
1353 {
1354  /* get a range to timestamps that can be used uniquely */
1355  time_t now = time(NULL);
1356  time_t start = now + MR_MIN(count, MR_MAX_SECONDS_TO_LEND_FROM_FUTURE) - count;
1357  start = MR_MAX(s_last_smeared_timestamp+1, start);
1358 
1359  s_last_smeared_timestamp = start+(count-1);
1360  return start;
1361 }
1362 
1363 
1364 time_t mr_smeared_time__(void)
1365 {
1366  /* function returns a corrected time(NULL) */
1367  time_t now = time(NULL);
1368  if( s_last_smeared_timestamp >= now ) {
1369  now = s_last_smeared_timestamp+1;
1370  }
1371  return now;
1372 }
1373 
1374 
1375 /*******************************************************************************
1376  * generate Message-IDs
1377  ******************************************************************************/
1378 
1379 
1380 static char* encode_66bits_as_base64(uint32_t v1, uint32_t v2, uint32_t fill /*only the lower 2 bits are used*/)
1381 {
1382  /* encode 66 bits as a base64 string. This is useful for ID generating with short strings as
1383  we save 5 character in each id compared to 64 bit hex encoding, for a typical group ID, these are 10 characters (grpid+msgid):
1384  hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters
1385  base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits) */
1386  char* ret = malloc(12); if( ret==NULL ) { exit(34); }
1387  static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1388  ret[ 0] = chars[ (v1>>26) & 0x3F ];
1389  ret[ 1] = chars[ (v1>>20) & 0x3F ];
1390  ret[ 2] = chars[ (v1>>14) & 0x3F ];
1391  ret[ 3] = chars[ (v1>> 8) & 0x3F ];
1392  ret[ 4] = chars[ (v1>> 2) & 0x3F ];
1393  ret[ 5] = chars[ ( (v1<< 4) & 0x30 ) | ( (v2>>28) & 0x0F ) ];
1394  ret[ 6] = chars[ (v2>>22) & 0x3F ];
1395  ret[ 7] = chars[ (v2>>16) & 0x3F ];
1396  ret[ 8] = chars[ (v2>>10) & 0x3F ];
1397  ret[ 9] = chars[ (v2>> 4) & 0x3F ];
1398  ret[10] = chars[ ( (v2<< 2) & 0x3C ) | (fill & 0x03) ];
1399  ret[11] = 0;
1400  return ret;
1401 }
1402 
1403 
1404 char* mr_create_id(void)
1405 {
1406  /* the generated ID should be as short and as unique as possible:
1407  - short, because it is also used as part of Message-ID headers
1408  - unique as two IDs generated on two devices should not be the same. However, collisions are not world-wide but only by the few contacts.
1409 
1410  Additional information:
1411  - for OUTGOING messages this ID is written to the header as `X-MrGrpId:` and is added to the message ID as Gr.<grpid>.<random>@<random>
1412  - for INCOMING messages, the ID is taken from the X-MrGrpId-header or from the Message-ID in the In-Reply-To: or References:-Header
1413  - the group ID should be a string with the characters [a-zA-Z0-9\-_] */
1414  uint32_t now = time(NULL);
1415  uint32_t pid = getpid();
1416  uint32_t rnd = random() ^ pid;
1417  return encode_66bits_as_base64(now, rnd, pid/*only the lower 2 bits are taken from this value*/);
1418 }
1419 
1420 
1421 char* mr_create_dummy_references_mid()
1422 {
1423  char* msgid = mr_create_id(), *ret = NULL;
1424  ret = mr_mprintf("Rf.%s@mr.thread", msgid);
1425  free(msgid);
1426  return ret;
1427 }
1428 
1429 
1430 char* mr_create_outgoing_rfc724_mid(const char* grpid, const char* from_addr)
1431 {
1432  /* Function generates a Message-ID that can be used for a new outgoing message.
1433  - this function is called for all outgoing messages.
1434  - the message ID should be globally unique
1435  - do not add a counter or any private data as as this may give unneeded information to the receiver */
1436 
1437  char* msgid = mr_create_id(), *ret = NULL;
1438  if( grpid ) {
1439  ret = mr_mprintf("Gr.%s.%s.%s", grpid, msgid, from_addr);
1440  /* ^^^ `Gr.` must never change as this is used to identify group messages in normal-clients-replies. The dot is choosen as this is normally not used for random ID creation. */
1441  }
1442  else {
1443  ret = mr_mprintf("Mr.%s.%s", msgid, from_addr);
1444  /* ^^^ `Mr.` is currently not used, however, this may change in future */
1445  }
1446  free(msgid);
1447  return ret;
1448 }
1449 
1450 
1451 char* mr_create_incoming_rfc724_mid(time_t message_timestamp, uint32_t contact_id_from, carray* contact_ids_to)
1452 {
1453  /* Function generates a Message-ID for incoming messages that lacks one.
1454  - normally, this function is not needed as incoming messages already have an ID
1455  - the generated ID is only for internal use; it should be database-unique
1456  - when fetching the same message again, this function should generate the same Message-ID
1457  */
1458 
1459  if( message_timestamp == MR_INVALID_TIMESTAMP || contact_ids_to == NULL || carray_count(contact_ids_to)==0 ) {
1460  return NULL;
1461  }
1462 
1463  /* find out the largets receiver ID (we could also take the smallest, but it should be unique) */
1464  size_t i, icnt = carray_count(contact_ids_to);
1465  uint32_t largest_id_to = 0;
1466  for( i = 0; i < icnt; i++ ) {
1467  uint32_t cur_id = (uint32_t)(uintptr_t)carray_get(contact_ids_to, i);
1468  if( cur_id > largest_id_to ) {
1469  largest_id_to = cur_id;
1470  }
1471  }
1472 
1473  /* build a more or less unique string based on the timestamp and one receiver -
1474  for our purposes, this seems "good enough" for the moment, esp. as clients normally set Message-ID on sent. */
1475  return mr_mprintf("%lu-%lu-%lu@stub", (unsigned long)message_timestamp, (unsigned long)contact_id_from, (unsigned long)largest_id_to);
1476 }
1477 
1478 
1479 char* mr_extract_grpid_from_rfc724_mid(const char* mid)
1480 {
1481  /* extract our group ID from Message-IDs as `Gr.12345678.morerandom.user@domain.de`; "12345678" is the wanted ID in this example. */
1482  int success = 0;
1483  char* ret = NULL, *p1;
1484 
1485  if( mid == NULL || strlen(mid)<8 || mid[0]!='G' || mid[1]!='r' || mid[2]!='.' ) {
1486  goto cleanup;
1487  }
1488 
1489  ret = safe_strdup(&mid[3]);
1490 
1491  p1 = strchr(ret, '.');
1492  if( p1 == NULL ) {
1493  goto cleanup;
1494  }
1495  *p1 = 0;
1496 
1497  if( strlen(ret)!=MR_VALID_ID_LEN ) {
1498  goto cleanup;
1499  }
1500 
1501  success = 1;
1502 
1503 cleanup:
1504  if( success == 0 ) { free(ret); ret = NULL; }
1505  return success? ret : NULL;
1506 }
1507 
1508 
1509 char* mr_extract_grpid_from_rfc724_mid_list(const clist* list)
1510 {
1511  clistiter* cur;
1512  if( list ) {
1513  for( cur = clist_begin(list); cur!=NULL ; cur=clist_next(cur) ) {
1514  const char* mid = clist_content(cur);
1515  char* grpid = mr_extract_grpid_from_rfc724_mid(mid);
1516  if( grpid ) {
1517  return grpid;
1518  }
1519  }
1520  }
1521  return NULL;
1522 }
1523 
1524 
1525 
1526 /*******************************************************************************
1527  * file tools
1528  ******************************************************************************/
1529 
1530 
1531 int mr_file_exist(const char* pathNfilename)
1532 {
1533  struct stat st;
1534  if( stat(pathNfilename, &st) == 0 ) {
1535  return 1; /* the size, however, may be 0 */
1536  }
1537  else {
1538  return 0;
1539  }
1540 }
1541 
1542 
1543 size_t mr_get_filebytes(const char* pathNfilename)
1544 {
1545  struct stat st;
1546  if( stat(pathNfilename, &st) == 0 ) {
1547  return (size_t)st.st_size;
1548  }
1549  else {
1550  return 0;
1551  }
1552 }
1553 
1554 
1555 char* mr_get_filename(const char* pathNfilename)
1556 {
1557  const char* p = strrchr(pathNfilename, '/');
1558  if( p==NULL ) {
1559  p = strrchr(pathNfilename, '\\');
1560  }
1561 
1562  if( p ) {
1563  p++;
1564  return safe_strdup(p);
1565  }
1566  else {
1567  return safe_strdup(pathNfilename);
1568  }
1569 }
1570 
1571 
1572 int mr_delete_file(const char* pathNfilename, mrmailbox_t* log/*may be NULL*/)
1573 {
1574  if( pathNfilename==NULL ) {
1575  return 0;
1576  }
1577 
1578  if( remove(pathNfilename)!=0 ) {
1579  mrmailbox_log_warning(log, 0, "Cannot delete \"%s\".", pathNfilename);
1580  return 0;
1581  }
1582 
1583  return 1;
1584 }
1585 
1586 
1587 int mr_copy_file(const char* src, const char* dest, mrmailbox_t* log/*may be NULL*/)
1588 {
1589  int success = 0, fd_src = -1, fd_dest = -1;
1590  #define MR_COPY_BUF_SIZE 4096
1591  char buf[MR_COPY_BUF_SIZE];
1592  size_t bytes_read;
1593  int anything_copied = 0;
1594 
1595  if( src==NULL || dest==NULL ) {
1596  return 0;
1597  }
1598 
1599  if( (fd_src=open(src, O_RDONLY)) < 0 ) {
1600  mrmailbox_log_error(log, 0, "Cannot open source file \"%s\".", src);
1601  goto cleanup;
1602  }
1603 
1604  if( (fd_dest=open(dest, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0 ) {
1605  mrmailbox_log_error(log, 0, "Cannot open destination file \"%s\".", dest);
1606  goto cleanup;
1607  }
1608 
1609  while( (bytes_read=read(fd_src, buf, MR_COPY_BUF_SIZE)) > 0 ) {
1610  if (write(fd_dest, buf, bytes_read) != bytes_read) {
1611  mrmailbox_log_error(log, 0, "Cannot write %i bytes to \"%s\".", bytes_read, dest);
1612  }
1613  anything_copied = 1;
1614  }
1615 
1616  if( !anything_copied ) {
1617  /* not a single byte copied -> check if the source is empty, too */
1618  close(fd_src);
1619  fd_src = -1;
1620  if( mr_get_filebytes(src)!=0 ) {
1621  mrmailbox_log_error(log, 0, "Different size informatio for \"%s\".", bytes_read, dest);
1622  goto cleanup;
1623  }
1624  }
1625 
1626  success = 1;
1627 
1628 cleanup:
1629  if( fd_src >= 0 ) { close(fd_src); }
1630  if( fd_dest >= 0 ) { close(fd_dest); }
1631  return success;
1632 }
1633 
1634 
1635 int mr_create_folder(const char* pathNfilename, mrmailbox_t* log)
1636 {
1637  struct stat st;
1638  if (stat(pathNfilename, &st) == -1) {
1639  if( mkdir(pathNfilename, 0755) != 0 ) {
1640  mrmailbox_log_warning(log, 0, "Cannot create directory \"%s\".", pathNfilename);
1641  return 0;
1642  }
1643  }
1644  return 1;
1645 }
1646 
1647 
1648 char* mr_get_filesuffix_lc(const char* pathNfilename)
1649 {
1650  if( pathNfilename ) {
1651  const char* p = strrchr(pathNfilename, '.'); /* use the last point, we're interesting the "main" type */
1652  if( p ) {
1653  p++;
1654  return mr_strlower(p); /* in contrast to mr_split_filename() we return the lowercase suffix */
1655  }
1656  }
1657  return NULL;
1658 }
1659 
1660 
1661 void mr_split_filename(const char* pathNfilename, char** ret_basename, char** ret_all_suffixes_incl_dot)
1662 {
1663  /* splits a filename into basename and all suffixes, eg. "/path/foo.tar.gz" is splitted into "foo.tar" and ".gz",
1664  (we use the _last_ dot which allows the usage inside the filename which are very usual;
1665  maybe the detection could be more intelligent, however, for the moment, it is just file)
1666  - if there is no suffix, the returned suffix string is empty, eg. "/path/foobar" is splitted into "foobar" and ""
1667  - the case of the returned suffix is preserved; this is to allow reconstruction of (similar) names */
1668  char* basename = mr_get_filename(pathNfilename), *suffix;
1669  char* p1 = strrchr(basename, '.');
1670  if( p1 ) {
1671  suffix = safe_strdup(p1);
1672  *p1 = 0;
1673  }
1674  else {
1675  suffix = safe_strdup(NULL);
1676  }
1677 
1678  /* return the given values */
1679  if( ret_basename ) { *ret_basename = basename; } else { free(basename); }
1680  if( ret_all_suffixes_incl_dot ) { *ret_all_suffixes_incl_dot = suffix; } else { free(suffix); }
1681 }
1682 
1683 
1684 
1685 void mr_validate_filename(char* filename)
1686 {
1687  /* function modifies the given buffer and replaces all characters not valid in filenames by a "-" */
1688  char* p1 = filename;
1689  while( *p1 ) {
1690  if( *p1=='/' || *p1=='\\' || *p1==':' ) {
1691  *p1 = '-';
1692  }
1693  p1++;
1694  }
1695 }
1696 
1697 
1698 char* mr_get_fine_pathNfilename(const char* folder, const char* desired_filenameNsuffix__)
1699 {
1700  char* ret = NULL, *filenameNsuffix, *basename = NULL, *dotNSuffix = NULL;
1701  time_t now = time(NULL);
1702  struct stat st;
1703  int i;
1704 
1705  filenameNsuffix = safe_strdup(desired_filenameNsuffix__);
1706  mr_validate_filename(filenameNsuffix);
1707  mr_split_filename(filenameNsuffix, &basename, &dotNSuffix);
1708 
1709  for( i = 0; i < 1000 /*no deadlocks, please*/; i++ ) {
1710  if( i ) {
1711  time_t idx = i<100? i : now+i;
1712  ret = mr_mprintf("%s/%s-%lu%s", folder, basename, (unsigned long)idx, dotNSuffix);
1713  }
1714  else {
1715  ret = mr_mprintf("%s/%s%s", folder, basename, dotNSuffix);
1716  }
1717  if (stat(ret, &st) == -1) {
1718  goto cleanup; /* fine filename found */
1719  }
1720  free(ret); /* try over with the next index */
1721  ret = NULL;
1722  }
1723 
1724 cleanup:
1725  free(filenameNsuffix);
1726  free(basename);
1727  free(dotNSuffix);
1728  return ret;
1729 }
1730 
1731 
1732 int mr_write_file(const char* pathNfilename, const void* buf, size_t buf_bytes, mrmailbox_t* log)
1733 {
1734  int success = 0;
1735 
1736  FILE* f = fopen(pathNfilename, "wb");
1737  if( f ) {
1738  if( fwrite(buf, 1, buf_bytes, f) == buf_bytes ) {
1739  success = 1;
1740  }
1741  else {
1742  mrmailbox_log_warning(log, 0, "Cannot write %lu bytes to \"%s\".", (unsigned long)buf_bytes, pathNfilename);
1743  }
1744  fclose(f);
1745  }
1746  else {
1747  mrmailbox_log_warning(log, 0, "Cannot open \"%s\" for writing.", pathNfilename);
1748  }
1749 
1750  return success;
1751 }
1752 
1753 
1754 int mr_read_file(const char* pathNfilename, void** buf, size_t* buf_bytes, mrmailbox_t* log)
1755 {
1756  int success = 0;
1757 
1758  if( pathNfilename==NULL || buf==NULL || buf_bytes==NULL ) {
1759  return 0; /* do not go to cleanup as this would dereference "buf" and "buf_bytes" */
1760  }
1761 
1762  *buf = NULL;
1763  *buf_bytes = 0;
1764  FILE* f = fopen(pathNfilename, "rb");
1765  if( f==NULL ) { goto cleanup; }
1766 
1767  fseek(f, 0, SEEK_END);
1768  *buf_bytes = ftell(f);
1769  fseek(f, 0, SEEK_SET);
1770  if( *buf_bytes <= 0 ) { goto cleanup; }
1771 
1772  *buf = malloc( (*buf_bytes) + 1 /*be pragmatic and terminate all files by a null - fine for texts and does not hurt for the rest */ );
1773  if( *buf==NULL ) { goto cleanup; }
1774 
1775  ((char*)*buf)[*buf_bytes /*we allocated one extra byte above*/] = 0;
1776 
1777  if( fread(*buf, 1, *buf_bytes, f)!=*buf_bytes ) { goto cleanup; }
1778 
1779  success = 1;
1780 
1781 cleanup:
1782  if( f ) {
1783  fclose(f);
1784  }
1785  if( success==0 ) {
1786  free(*buf);
1787  *buf = NULL;
1788  *buf_bytes = 0;
1789  mrmailbox_log_warning(log, 0, "Cannot read \"%s\" or file is empty.", pathNfilename);
1790  }
1791  return success; /* buf must be free()'d by the caller */
1792 }
1793 
1794 
1795 int mr_get_filemeta(const void* buf_start, size_t buf_bytes, uint32_t* ret_width, uint32_t *ret_height)
1796 {
1797  /* Strategy:
1798  reading GIF dimensions requires the first 10 bytes of the file
1799  reading PNG dimensions requires the first 24 bytes of the file
1800  reading JPEG dimensions requires scanning through jpeg chunks
1801  In all formats, the file is at least 24 bytes big, so we'll read that always
1802  inspired by http://www.cplusplus.com/forum/beginner/45217/ */
1803  const unsigned char* buf = buf_start;
1804  if (buf_bytes<24) {
1805  return 0;
1806  }
1807 
1808  /* For JPEGs, we need to check the first bytes of each DCT chunk. */
1809  if( buf[0]==0xFF && buf[1]==0xD8 && buf[2]==0xFF )
1810  {
1811  long pos = 2;
1812  while( buf[pos]==0xFF )
1813  {
1814  if (buf[pos+1]==0xC0 || buf[pos+1]==0xC1 || buf[pos+1]==0xC2 || buf[pos+1]==0xC3 || buf[pos+1]==0xC9 || buf[pos+1]==0xCA || buf[pos+1]==0xCB) {
1815  *ret_height = (buf[pos+5]<<8) + buf[pos+6]; /* sic! height is first */
1816  *ret_width = (buf[pos+7]<<8) + buf[pos+8];
1817  return 1;
1818  }
1819  pos += 2+(buf[pos+2]<<8)+buf[pos+3];
1820  if (pos+12>buf_bytes) { break; }
1821  }
1822  }
1823 
1824  /* GIF: first three bytes say "GIF", next three give version number. Then dimensions */
1825  if( buf[0]=='G' && buf[1]=='I' && buf[2]=='F' )
1826  {
1827  *ret_width = buf[6] + (buf[7]<<8);
1828  *ret_height = buf[8] + (buf[9]<<8);
1829  return 1;
1830  }
1831 
1832  /* PNG: the first frame is by definition an IHDR frame, which gives dimensions */
1833  if( buf[0]==0x89 && buf[1]=='P' && buf[2]=='N' && buf[3]=='G' && buf[4]==0x0D && buf[5]==0x0A && buf[6]==0x1A && buf[7]==0x0A
1834  && buf[12]=='I' && buf[13]=='H' && buf[14]=='D' && buf[15]=='R' )
1835  {
1836  *ret_width = (buf[16]<<24) + (buf[17]<<16) + (buf[18]<<8) + (buf[19]<<0);
1837  *ret_height = (buf[20]<<24) + (buf[21]<<16) + (buf[22]<<8) + (buf[23]<<0);
1838  return 1;
1839  }
1840 
1841  return 0;
1842 }
An object representing a single mailbox.
Definition: mrmailbox.h:141
-
- - - -