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