1 /* BMailProtocol - the base class for protocol filters 2 ** 3 ** Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. 4 */ 5 6 7 #include <String.h> 8 #include <Alert.h> 9 #include <Query.h> 10 #include <VolumeRoster.h> 11 #include <StringList.h> 12 #include <E-mail.h> 13 #include <NodeInfo.h> 14 #include <Directory.h> 15 #include <NodeMonitor.h> 16 #include <Node.h> 17 18 #include <stdio.h> 19 #include <fs_attr.h> 20 #include <malloc.h> 21 #include <assert.h> 22 23 #include <MDRLanguage.h> 24 25 class BMailProtocol; 26 27 #include <MailProtocol.h> 28 #include <ChainRunner.h> 29 #include <status.h> 30 31 namespace { 32 33 class ManifestAdder : public BMailChainCallback { 34 public: 35 ManifestAdder(BStringList *list,BStringList **list2, const char *id) : manifest(list), uids_on_disk(list2), uid(id) {} 36 virtual void Callback(status_t result) { 37 if (result == B_OK) { 38 (*manifest) += uid; 39 if (*uids_on_disk != NULL) 40 (**uids_on_disk) += uid; 41 } 42 } 43 44 private: 45 BStringList *manifest,**uids_on_disk; 46 const char *uid; 47 }; 48 49 class MessageDeletion : public BMailChainCallback { 50 public: 51 MessageDeletion(BMailProtocol *home, const char *uid, BEntry *io_entry, bool delete_anyway); 52 virtual void Callback(status_t result); 53 54 private: 55 BMailProtocol *us; 56 bool always; 57 const char *message_id; 58 BEntry *entry; 59 }; 60 61 62 inline void 63 BMailProtocol::error_alert(const char *process, status_t error) 64 { 65 BString string; 66 MDR_DIALECT_CHOICE ( 67 string << "Error while " << process << ": " << strerror(error); 68 runner->ShowError(string.String()); 69 , 70 string << process << "中にエラーが発生しました: " << strerror(error); 71 runner->ShowError(string.String()); 72 ) 73 } 74 75 76 class DeleteHandler : public BHandler { 77 public: 78 DeleteHandler(BMailProtocol *a) 79 : us(a) 80 { 81 } 82 83 void MessageReceived(BMessage *msg) 84 { 85 if ((msg->what == 'DELE') && (us->InitCheck() == B_OK)) { 86 us->CheckForDeletedMessages(); 87 Looper()->RemoveHandler(this); 88 delete this; 89 } 90 } 91 92 private: 93 BMailProtocol *us; 94 }; 95 96 class TrashMonitor : public BHandler { 97 public: 98 TrashMonitor(BMailProtocol *a, int32 chain_id) 99 : us(a), trash("/boot/home/Desktop/Trash"), messages_for_us(0), id(chain_id) 100 { 101 } 102 103 void MessageReceived(BMessage *msg) 104 { 105 if (msg->what == 'INIT') { 106 node_ref to_watch; 107 trash.GetNodeRef(&to_watch); 108 watch_node(&to_watch,B_WATCH_DIRECTORY,this); 109 return; 110 } 111 if ((msg->what == B_NODE_MONITOR) && (us->InitCheck() == B_OK)) { 112 int32 opcode; 113 if (msg->FindInt32("opcode",&opcode) < B_OK) 114 return; 115 116 if (opcode == B_ENTRY_MOVED) { 117 int64 node(msg->FindInt64("to directory")); 118 dev_t device(msg->FindInt32("device")); 119 node_ref item_ref; 120 item_ref.node = node; 121 item_ref.device = device; 122 123 BDirectory moved_to(&item_ref); 124 125 BNode trash_item(&moved_to,msg->FindString("name")); 126 int32 chain; 127 if (trash_item.ReadAttr("MAIL:chain",B_INT32_TYPE,0,&chain,sizeof(chain)) < B_OK) 128 return; 129 130 if (chain == id) 131 messages_for_us += (moved_to == trash) ? 1 : -1; 132 } 133 134 if (messages_for_us < 0) 135 messages_for_us = 0; // Guard against weirdness 136 137 if (trash.CountEntries() == 0) { 138 if (messages_for_us > 0) 139 us->CheckForDeletedMessages(); 140 141 messages_for_us = 0; 142 } 143 } 144 } 145 146 private: 147 BMailProtocol *us; 148 BDirectory trash; 149 int32 messages_for_us; 150 int32 id; 151 }; 152 153 BMailProtocol::BMailProtocol(BMessage *settings, BMailChainRunner *run) 154 : BMailFilter(settings), 155 runner(run), trash_monitor(NULL), uids_on_disk(NULL) 156 { 157 unique_ids = new BStringList; 158 BMailProtocol::settings = settings; 159 160 manifest = new BStringList; 161 162 { 163 BString attr_name = "MAIL:"; 164 attr_name << runner->Chain()->ID() << ":manifest"; //--- In case someone puts multiple accounts in the same directory 165 166 if (runner->Chain()->MetaData()->HasString("path")) { 167 BNode node(runner->Chain()->MetaData()->FindString("path")); 168 if (node.InitCheck() >= B_OK) { 169 attr_info info; 170 if (node.GetAttrInfo(attr_name.String(),&info) < B_OK) { 171 if (runner->Chain()->MetaData()->FindFlat("manifest", manifest) == B_OK) { 172 runner->Chain()->MetaData()->RemoveName("manifest"); 173 runner->Chain()->Save(); //--- Not having this code made an earlier version of MDR delete all my *(&(*& mail 174 } 175 } else { 176 void *flatmanifest = malloc(info.size); 177 node.ReadAttr(attr_name.String(),manifest->TypeCode(),0,flatmanifest,info.size); 178 manifest->Unflatten(manifest->TypeCode(),flatmanifest,info.size); 179 free(flatmanifest); 180 } 181 } else runner->ShowError("Error while reading account manifest: cannot use destination directory."); 182 } else runner->ShowError("Error while reading account manifest: no destination directory exists."); 183 } 184 185 uids_on_disk = new BStringList; 186 BVolumeRoster volumes; 187 BVolume volume; 188 while (volumes.GetNextVolume(&volume) == B_OK) { 189 BQuery fido; 190 entry_ref entry; 191 192 fido.SetVolume(&volume); 193 fido.PushAttr("MAIL:chain"); 194 fido.PushInt32(settings->FindInt32("chain")); 195 fido.PushOp(B_EQ); 196 fido.PushAttr("MAIL:pending_chain"); 197 fido.PushInt32(settings->FindInt32("chain")); 198 fido.PushOp(B_EQ); 199 fido.PushOp(B_OR); 200 if (!settings->FindBool("leave_mail_on_server")) { 201 fido.PushAttr("BEOS:type"); 202 fido.PushString("text/x-partial-email"); 203 fido.PushOp(B_EQ); 204 fido.PushOp(B_AND); 205 } 206 fido.Fetch(); 207 208 BString uid; 209 while (fido.GetNextRef(&entry) == B_OK) { 210 BNode(&entry).ReadAttrString("MAIL:unique_id",&uid); 211 uids_on_disk->AddItem(uid.String()); 212 } 213 } 214 215 (*manifest) |= (*uids_on_disk); 216 217 if (!settings->FindBool("login_and_do_nothing_else_of_any_importance")) { 218 DeleteHandler *h = new DeleteHandler(this); 219 runner->AddHandler(h); 220 runner->PostMessage('DELE',h); 221 222 trash_monitor = new TrashMonitor(this,runner->Chain()->ID()); 223 runner->AddHandler(trash_monitor); 224 runner->PostMessage('INIT',trash_monitor); 225 } 226 } 227 228 229 BMailProtocol::~BMailProtocol() 230 { 231 if (manifest != NULL) { 232 BMessage *meta_data = runner->Chain()->MetaData(); 233 meta_data->RemoveName("manifest"); 234 BString attr_name = "MAIL:"; 235 attr_name << runner->Chain()->ID() << ":manifest"; //--- In case someone puts multiple accounts in the same directory 236 if (meta_data->HasString("path")) { 237 BNode node(meta_data->FindString("path")); 238 if (node.InitCheck() >= B_OK) { 239 node.RemoveAttr(attr_name.String()); 240 ssize_t manifestsize = manifest->FlattenedSize(); 241 void *flatmanifest = malloc(manifestsize); 242 manifest->Flatten(flatmanifest,manifestsize); 243 if (status_t err = node.WriteAttr(attr_name.String(),manifest->TypeCode(),0,flatmanifest,manifestsize) < B_OK) { 244 BString error = "Error while saving account manifest: "; 245 error << strerror(err); 246 runner->ShowError(error.String()); 247 } 248 free(flatmanifest); 249 } else runner->ShowError("Error while saving account manifest: cannot use destination directory."); 250 } else runner->ShowError("Error while saving account manifest: no destination directory exists."); 251 } 252 delete unique_ids; 253 delete manifest; 254 delete trash_monitor; 255 delete uids_on_disk; 256 } 257 258 259 #define dump_stringlist(a) printf("BStringList %s:\n",#a); \ 260 for (int32 i = 0; i < (a)->CountItems(); i++)\ 261 puts((a)->ItemAt(i)); \ 262 puts("Done\n"); 263 264 status_t 265 BMailProtocol::ProcessMailMessage(BPositionIO **io_message, BEntry *io_entry, 266 BMessage *io_headers, BPath *io_folder, const char *io_uid) 267 { 268 status_t error; 269 270 if (io_uid == NULL) 271 return B_ERROR; 272 273 error = GetMessage(io_uid, io_message, io_headers, io_folder); 274 if (error < B_OK) { 275 if (error != B_MAIL_END_FETCH) { 276 MDR_DIALECT_CHOICE ( 277 error_alert("getting a message",error);, 278 error_alert("新しいメッセージヲ取得中にエラーが発生しました",error); 279 ); 280 } 281 return B_MAIL_END_FETCH; 282 } 283 284 runner->RegisterMessageCallback(new ManifestAdder(manifest, &uids_on_disk, io_uid)); 285 runner->RegisterMessageCallback(new MessageDeletion(this, io_uid, io_entry, !settings->FindBool("leave_mail_on_server"))); 286 287 return B_OK; 288 } 289 290 void BMailProtocol::CheckForDeletedMessages() { 291 { 292 //---Delete things from the manifest no longer on the server 293 BStringList temp; 294 manifest->NotThere(*unique_ids, &temp); 295 (*manifest) -= temp; 296 } 297 298 if (((settings->FindBool("delete_remote_when_local")) || !(settings->FindBool("leave_mail_on_server"))) && (manifest->CountItems() > 0)) { 299 BStringList to_delete; 300 301 if (uids_on_disk == NULL) { 302 BStringList query_contents; 303 BVolumeRoster volumes; 304 BVolume volume; 305 306 while (volumes.GetNextVolume(&volume) == B_OK) { 307 BQuery fido; 308 entry_ref entry; 309 310 fido.SetVolume(&volume); 311 fido.PushAttr("MAIL:chain"); 312 fido.PushInt32(settings->FindInt32("chain")); 313 fido.PushOp(B_EQ); 314 fido.PushAttr("MAIL:pending_chain"); 315 fido.PushInt32(settings->FindInt32("chain")); 316 fido.PushOp(B_EQ); 317 fido.PushOp(B_OR); 318 fido.Fetch(); 319 320 BString uid; 321 while (fido.GetNextRef(&entry) == B_OK) { 322 BNode(&entry).ReadAttrString("MAIL:unique_id",&uid); 323 query_contents.AddItem(uid.String()); 324 } 325 } 326 327 query_contents.NotHere(*manifest,&to_delete); 328 } else { 329 uids_on_disk->NotHere(*manifest,&to_delete); 330 delete uids_on_disk; 331 uids_on_disk = NULL; 332 } 333 334 for (int32 i = 0; i < to_delete.CountItems(); i++) 335 DeleteMessage(to_delete[i]); 336 337 //*(unique_ids) -= to_delete; --- This line causes bad things to 338 // happen (POP3 client uses the wrong indices to retrieve 339 // messages). Without it, bad things don't happen. 340 *(manifest) -= to_delete; 341 } 342 } 343 344 void BMailProtocol::_ReservedProtocol1() {} 345 void BMailProtocol::_ReservedProtocol2() {} 346 void BMailProtocol::_ReservedProtocol3() {} 347 void BMailProtocol::_ReservedProtocol4() {} 348 void BMailProtocol::_ReservedProtocol5() {} 349 350 351 // #pragma mark - 352 353 354 MessageDeletion::MessageDeletion(BMailProtocol *home, const char *uid, 355 BEntry *io_entry, bool delete_anyway) 356 : 357 us(home), 358 always(delete_anyway), 359 message_id(uid), entry(io_entry) 360 { 361 } 362 363 364 void 365 MessageDeletion::Callback(status_t result) 366 { 367 #if DEBUG 368 printf("Deleting %s\n", message_id); 369 #endif 370 BNode node(entry); 371 BNodeInfo info(&node); 372 char type[255]; 373 info.GetType(type); 374 if ((always && strcmp(B_MAIL_TYPE,type) == 0) || result == B_MAIL_DISCARD) 375 us->DeleteMessage(message_id); 376 } 377 378 } 379