1 /// 2 module imap.session; 3 import imap.defines; 4 import imap.socket; 5 import imap.set; 6 import imap.sil : SILdoc; 7 8 import core.stdc.stdio; 9 import core.stdc.string; 10 import core.stdc.errno; 11 import std.socket; 12 import core.time : Duration; 13 14 import deimos.openssl.ssl; 15 import deimos.openssl.err; 16 import deimos.openssl.sha; 17 18 19 struct SSL_ 20 { 21 SSL* handle; 22 alias handle this; 23 } 24 25 26 /// 27 Set!T removeValues(T)(Set!T set, T[] values) 28 { 29 import std.algorithm : each; 30 Set!T ret; 31 set.values_.byKeyValue.each!(entry => ret[entry.key] = entry.value); 32 foreach(value;values) 33 { 34 if (value in ret) 35 ret.remove(value); 36 } 37 return ret; 38 } 39 40 /// 41 Set!T addValues(T)(Set!T set, T[] values) 42 { 43 import std.algorithm : each; 44 Set!T ret; 45 set.values_.byKeyValue.each!(entry => ret[entry.key] = entry.value); 46 values.each!(value => set.values_[value]=true); 47 return ret; 48 } 49 50 /// 51 Set!T addSet(T)(Set!T lhs, Set!T rhs) 52 { 53 import std.algorithm : each; 54 Set!T ret; 55 return lhs.addValues(rhs.values); 56 } 57 58 /// 59 Set!T removeSet(T)(Set!T lhs, Set!T rhs) 60 { 61 import std.algorithm : each; 62 Set!T ret; 63 return lhs.removeValues(rhs.values); 64 } 65 66 /// 67 struct ImapServer 68 { 69 string server = "imap.fastmail.com"; // localhost"; 70 string port = "993"; 71 72 string toString() 73 { 74 import std.format : format; 75 return format!"%s:%s"(server,port); 76 } 77 } 78 79 /// 80 struct ImapLogin 81 { 82 @SILdoc("User's name. It takes a string as a value.") 83 string username = "laeeth@kaleidic.io"; 84 85 @SILdoc("User's secret keyword. If a password wasn't supplied the user will be asked to enter one interactively the first time it will be needed. It takes a string as a value.") 86 string password; 87 88 string toString() 89 { 90 import std.format : format; 91 return format!"%s:[hidden]"(username); 92 } 93 } 94 95 /// 96 struct Options 97 { 98 import core.time : Duration, seconds, minutes; 99 100 bool debugMode = true; 101 bool verboseOutput = true; 102 bool interactive = false; 103 bool namespace = false; 104 105 @SILdoc("When this option is enabled and the server supports the Challenge-Response Authentication Mechanism (specifically CRAM-MD5), this method will be used for user authentication instead of a plaintext password LOGIN. This variable takes a boolean as a value. Default is false") 106 bool cramMD5 = false; 107 108 bool startTLS = false; 109 bool tryCreate = false; 110 bool recoverAll = true; 111 bool recoverErrors = true; 112 113 @SILdoc("Normally, messages are marked for deletion and are actually deleted when the mailbox is closed. When this option is enabled, messages are expunged immediately after being marked deleted. This variable takes a boolean as a value. Default is false") 114 bool expunge = false; 115 116 @SILdoc("By enabling this option new mailboxes that were automatically created, get also subscribed; they are set active in order for IMAP clients to recognize them. This variable takes a boolean as a value. Default is false") 117 bool subscribe = false; 118 119 bool wakeOnAny = true; 120 121 @SILdoc("The time in minutes before terminating and re-issuing the IDLE command, in order to keep alive the connection, by resetting the inactivity timeout of the server. A standards compliant server must have an inactivity timeout of at least 30 minutes. But it may happen that some IMAP servers don't respect that, or some intermediary network device has a shorter timeout. By setting this option the above problem can be worked around. This variable takes a number as a value. Default is 29 minutes. ") 122 Duration keepAlive = 29.minutes; 123 124 string logFile; 125 string configFile; 126 string oneline; 127 string trustStore = "/etc/ssl/certs"; 128 string trustFile = "/etc/ssl/certs/cert.pem"; 129 Duration timeout = 20.seconds; 130 } 131 132 /// 133 struct Session 134 { 135 import imap.defines : ImapStatus; 136 import imap.namespace; 137 Options options; 138 ImapStatus status_; 139 string server; 140 141 @SILdoc("The port to connect to. It takes a number as a value. Default is ''143'' for imap and ''993'' for imaps.") 142 string port; 143 144 package AddressInfo addressInfo; 145 ImapLogin imapLogin; 146 Socket socket; 147 ImapProtocol imapProtocol; 148 Set!Capability capabilities; 149 string namespacePrefix; 150 char namespaceDelim = '\0'; 151 Mailbox selected; 152 153 bool useSSL = true; 154 bool noCerts = true; 155 ProtocolSSL sslProtocol = ProtocolSSL.tls1_2; // ssl3; // tls1_2; 156 SSL* sslConnection; 157 SSL_CTX* sslContext; 158 159 string toString() 160 { 161 import std.array : Appender; 162 import std.format : format; 163 import std.conv : to; 164 Appender!string ret; 165 ret.put(format!"Session to %s:%s as user %s\n"(server,port,imapLogin.username)); 166 ret.put(format!"- useSSL: %s\n"(useSSL.to!string)); 167 ret.put(format!"- startTLS: %s\n"(useSSL.to!string)); 168 ret.put(format!"- noCerts: %s\n"(noCerts.to!string)); 169 ret.put(format!"- sslProtocol: %s\n"(sslProtocol.to!string)); 170 ret.put(format!"- imap protocol: %s\n"(imapProtocol.to!string)); 171 ret.put(format!" - capabilities: %s\n"(capabilities.to!string)); 172 ret.put(format!" - namespace: %s/%s\n"(namespacePrefix,[namespaceDelim])); 173 ret.put(format!" - selected mailbox: %s\n"(selected)); 174 return ret.data; 175 } 176 177 this(ImapServer imapServer,ImapLogin imapLogin, bool useSSL = true, Options options = Options.init) 178 { 179 import std.exception : enforce; 180 import std.process : environment; 181 this.options = options; 182 this.server = imapServer.server; 183 this.port = imapServer.port; 184 this.useSSL = useSSL; 185 this.imapLogin = imapLogin; 186 } 187 188 ref Session useStartTLS(bool useTLS = true) 189 { 190 this.options.startTLS = useTLS; 191 return this; 192 } 193 194 ref Session setSelected(Mailbox mailbox) 195 { 196 this.selected = mailbox; 197 return this; 198 } 199 200 ref Session setStatus(ImapStatus status) 201 { 202 this.status_ = status; 203 return this; 204 } 205 206 string status() 207 { 208 import std.conv : to; 209 return status_.to!string; 210 } 211 } 212