1/*
2 * Copyright 2007-2015, Haiku, Inc. All rights reserved.
3 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "AutoConfigView.h"
9
10#include <pwd.h>
11
12#include <Catalog.h>
13#include <Directory.h>
14#include <Entry.h>
15#include <FindDirectory.h>
16#include <LayoutBuilder.h>
17#include <Message.h>
18#include <MenuItem.h>
19#include <Path.h>
20#include <PopUpMenu.h>
21#include <String.h>
22#include <Window.h>
23
24#include <MailSettings.h>
25
26
27#undef B_TRANSLATION_CONTEXT
28#define B_TRANSLATION_CONTEXT "E-Mail"
29
30
31AutoConfigView::AutoConfigView(AutoConfig &config)
32	:
33	BBox("auto config"),
34	fAutoConfig(config)
35{
36	// Search for SMTP entry_ref
37	_GetSMTPAddOnRef(&fSMTPAddOnRef);
38
39	fInProtocolsField = new BMenuField(NULL, NULL, _SetupProtocolMenu());
40
41	fEmailView = new BTextControl("email", B_TRANSLATE("E-mail address:"),
42		"", new BMessage(kEMailChangedMsg));
43
44	fLoginNameView = new BTextControl("login", B_TRANSLATE("Login name:"),
45		"", NULL);
46
47	fPasswordView = new BTextControl("password", B_TRANSLATE("Password:"),
48		"", NULL);
49	fPasswordView->TextView()->HideTyping(true);
50
51	fAccountNameView = new BTextControl("account", B_TRANSLATE("Account name:"),
52		"", NULL);
53
54	fNameView = new BTextControl("name", B_TRANSLATE("Real name:"), "", NULL);
55
56	struct passwd* passwd = getpwent();
57	if (passwd != NULL)
58		fNameView->SetText(passwd->pw_gecos);
59
60	AddChild(BLayoutBuilder::Grid<>()
61		.SetInsets(B_USE_DEFAULT_SPACING)
62		.SetSpacing(B_USE_HALF_ITEM_SPACING, B_USE_HALF_ITEM_SPACING)
63
64		.Add(fInProtocolsField->CreateLabelLayoutItem(), 0, 0)
65		.Add(fInProtocolsField->CreateMenuBarLayoutItem(), 1, 0)
66
67		.Add(fEmailView->CreateLabelLayoutItem(), 0, 1)
68		.Add(fEmailView->CreateTextViewLayoutItem(), 1, 1)
69
70		.Add(fLoginNameView->CreateLabelLayoutItem(), 0, 2)
71		.Add(fLoginNameView->CreateTextViewLayoutItem(), 1, 2)
72
73		.Add(fPasswordView->CreateLabelLayoutItem(), 0, 3)
74		.Add(fPasswordView->CreateTextViewLayoutItem(), 1, 3)
75
76		.Add(fAccountNameView->CreateLabelLayoutItem(), 0, 4)
77		.Add(fAccountNameView->CreateTextViewLayoutItem(), 1, 4)
78
79		.Add(fNameView->CreateLabelLayoutItem(), 0, 5)
80		.Add(fNameView->CreateTextViewLayoutItem(), 1, 5)
81		.View());
82}
83
84
85void
86AutoConfigView::AttachedToWindow()
87{
88	// Resize the view to fit the contents properly
89	BRect rect = Bounds();
90	float newHeight = fNameView->Frame().bottom + 20 + 2;
91	newHeight += InnerFrame().top;
92	ResizeTo(rect.Width(), newHeight);
93
94	AdoptParentColors();
95	fEmailView->SetTarget(this);
96	fEmailView->MakeFocus(true);
97}
98
99
100void
101AutoConfigView::MessageReceived(BMessage *msg)
102{
103	switch (msg->what) {
104		case kEMailChangedMsg:
105		{
106			BString text = fLoginNameView->Text();
107			if (text == "")
108				_ProposeUsername();
109			fLoginNameView->MakeFocus();
110			fLoginNameView->TextView()->SelectAll();
111
112			text = fAccountNameView->Text();
113			if (text == "")
114				fAccountNameView->SetText(fEmailView->Text());
115			break;
116		}
117		default:
118			BView::MessageReceived(msg);
119			break;
120	}
121}
122
123
124bool
125AutoConfigView::GetBasicAccountInfo(account_info &info)
126{
127	status_t status = B_OK;
128
129	BString inboundProtocolName = "";
130	BMenuItem* item = fInProtocolsField->Menu()->FindMarked();
131	if (item) {
132		inboundProtocolName = item->Label();
133		item->Message()->FindRef("protocol", &(info.inboundProtocol));
134	}
135	else
136		status = B_ERROR;
137
138	if (inboundProtocolName.FindFirst("IMAP") >= 0)
139		info.inboundType = IMAP;
140	else
141		info.inboundType = POP;
142
143	info.outboundProtocol = fSMTPAddOnRef;
144	info.name = fNameView->Text();
145	info.accountName = fAccountNameView->Text();
146	info.email = fEmailView->Text();
147	info.loginName = fLoginNameView->Text();
148	info.password = fPasswordView->Text();
149
150	return status;
151}
152
153
154BPopUpMenu*
155AutoConfigView::_SetupProtocolMenu()
156{
157	BPopUpMenu* menu = new BPopUpMenu(B_TRANSLATE("Choose Protocol"));
158
159	// TODO: use path finder!
160	for (int i = 0; i < 2; i++) {
161		BPath path;
162		status_t status = find_directory((i == 0) ? B_USER_ADDONS_DIRECTORY :
163			B_BEOS_ADDONS_DIRECTORY, &path);
164		if (status != B_OK)
165			return menu;
166
167		path.Append("mail_daemon");
168		path.Append("inbound_protocols");
169
170		BDirectory dir(path.Path());
171		entry_ref protocolRef;
172		while (dir.GetNextRef(&protocolRef) == B_OK)
173		{
174			BEntry entry(&protocolRef);
175
176			BMessage* msg = new BMessage(kProtokollChangedMsg);
177			BMenuItem* item = new BMenuItem(entry.Name(), msg);
178			menu->AddItem(item);
179			msg->AddRef("protocol", &protocolRef);
180
181			item->SetMarked(true);
182		}
183	}
184
185	// make imap default protocol if existing
186	BMenuItem* imapItem =  menu->FindItem("IMAP");
187	if (imapItem)
188		imapItem->SetMarked(true);
189
190	return menu;
191}
192
193
194status_t
195AutoConfigView::_GetSMTPAddOnRef(entry_ref *ref)
196{
197	directory_which which[] = {
198		B_USER_ADDONS_DIRECTORY,
199		B_BEOS_ADDONS_DIRECTORY
200	};
201
202	for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
203		BPath path;
204		status_t status = find_directory(which[i], &path);
205		if (status != B_OK)
206			return B_ERROR;
207
208		path.Append("mail_daemon");
209		path.Append("outbound_protocols");
210		path.Append("SMTP");
211
212		BEntry entry(path.Path());
213		if (entry.Exists() && entry.GetRef(ref) == B_OK)
214			return B_OK;
215	}
216
217	return B_ENTRY_NOT_FOUND;
218}
219
220
221BString
222AutoConfigView::_ExtractLocalPart(const char* email)
223{
224	const char* at = strrchr(email, '@');
225	return BString(email, at - email);
226}
227
228
229void
230AutoConfigView::_ProposeUsername()
231{
232	const char* email = fEmailView->Text();
233	provider_info info;
234	status_t status = fAutoConfig.GetInfoFromMailAddress(email, &info);
235	if (status == B_OK) {
236		BString localPart = _ExtractLocalPart(email);
237		switch (info.username_pattern) {
238			case 0:
239				// username is the mail address
240				fLoginNameView->SetText(email);
241				break;
242			case 1:
243				// username is the local-part
244				fLoginNameView->SetText(localPart.String());
245				break;
246			case 2:
247				// do nothing
248				break;
249		}
250	}
251	else {
252		fLoginNameView->SetText(email);
253	}
254}
255
256
257bool
258AutoConfigView::IsValidMailAddress(BString email)
259{
260	int32 atPos = email.FindFirst("@");
261	if (atPos < 0)
262		return false;
263	BString provider;
264	email.CopyInto(provider, atPos + 1, email.Length() - atPos);
265	if (provider.FindLast(".") < 0)
266		return false;
267	return true;
268}
269
270
271// #pragma mark -
272
273
274ServerSettingsView::ServerSettingsView(const account_info &info)
275	:
276	BGroupView("server", B_VERTICAL),
277	fInboundAccount(true),
278	fOutboundAccount(true),
279	fInboundAuthMenu(NULL),
280	fOutboundAuthMenu(NULL),
281	fInboundEncrItemStart(NULL),
282	fOutboundEncrItemStart(NULL),
283	fImageID(-1)
284{
285	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
286
287	fInboundAccount = true;
288	fOutboundAccount = true;
289
290	// inbound
291	BBox* box = new BBox("inbound");
292	box->SetLabel(B_TRANSLATE("Incoming"));
293	AddChild(box);
294
295	BString serverName;
296	if (info.inboundType == IMAP)
297		serverName = info.providerInfo.imap_server;
298	else
299		serverName = info.providerInfo.pop_server;
300
301	BGridView* grid = new BGridView("inner");
302	grid->GridLayout()->SetInsets(B_USE_DEFAULT_SPACING);
303	box->AddChild(grid);
304
305	fInboundNameView = new BTextControl("inbound", B_TRANSLATE("Server Name:"),
306		serverName, new BMessage(kServerChangedMsg));
307	grid->GridLayout()->AddItem(fInboundNameView->CreateLabelLayoutItem(),
308		0, 0);
309	grid->GridLayout()->AddItem(fInboundNameView->CreateTextViewLayoutItem(),
310		1, 0);
311
312	int32 row = 1;
313
314	_GetAuthEncrMenu(info.inboundProtocol, fInboundAuthMenu,
315		fInboundEncryptionMenu);
316	if (fInboundAuthMenu != NULL) {
317		int authID = info.providerInfo.authentification_pop;
318		if (info.inboundType == POP)
319			fInboundAuthMenu->Menu()->ItemAt(authID)->SetMarked(true);
320		fInboundAuthItemStart = fInboundAuthMenu->Menu()->FindMarked();
321
322		grid->GridLayout()->AddItem(fInboundAuthMenu->CreateLabelLayoutItem(),
323			0, row);
324		grid->GridLayout()->AddItem(fInboundAuthMenu->CreateMenuBarLayoutItem(),
325			1, row++);
326	}
327	if (fInboundEncryptionMenu != NULL) {
328		BMenuItem *item = NULL;
329		if (info.inboundType == POP) {
330			item = fInboundEncryptionMenu->Menu()->ItemAt(
331				info.providerInfo.ssl_pop);
332			if (item != NULL)
333				item->SetMarked(true);
334		}
335		if (info.inboundType == IMAP) {
336			item = fInboundEncryptionMenu->Menu()->ItemAt(
337				info.providerInfo.ssl_imap);
338			if (item != NULL)
339				item->SetMarked(true);
340		}
341		fInboundEncrItemStart = fInboundEncryptionMenu->Menu()->FindMarked();
342
343		grid->GridLayout()->AddItem(
344			fInboundEncryptionMenu->CreateLabelLayoutItem(), 0, row);
345		grid->GridLayout()->AddItem(
346			fInboundEncryptionMenu->CreateMenuBarLayoutItem(), 1, row++);
347	}
348	grid->GridLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0, row);
349
350	if (!fInboundAccount)
351		box->Hide();
352
353	// outbound
354	box = new BBox("outbound");
355	box->SetLabel(B_TRANSLATE("Outgoing"));
356	AddChild(box);
357
358	grid = new BGridView("inner");
359	grid->GridLayout()->SetInsets(B_USE_DEFAULT_SPACING);
360	box->AddChild(grid);
361
362	serverName = info.providerInfo.smtp_server;
363	fOutboundNameView = new BTextControl("outbound",
364		B_TRANSLATE("Server name:"), serverName.String(),
365		new BMessage(kServerChangedMsg));
366	grid->GridLayout()->AddItem(fOutboundNameView->CreateLabelLayoutItem(),
367		0, 0);
368	grid->GridLayout()->AddItem(fOutboundNameView->CreateTextViewLayoutItem(),
369		1, 0);
370
371	row = 1;
372
373	_GetAuthEncrMenu(info.outboundProtocol, fOutboundAuthMenu,
374		fOutboundEncryptionMenu);
375	if (fOutboundAuthMenu != NULL) {
376		BMenuItem* item = fOutboundAuthMenu->Menu()->ItemAt(
377			info.providerInfo.authentification_smtp);
378		if (item != NULL)
379			item->SetMarked(true);
380		fOutboundAuthItemStart = item;
381
382		grid->GridLayout()->AddItem(fOutboundAuthMenu->CreateLabelLayoutItem(),
383			0, row);
384		grid->GridLayout()->AddItem(
385			fOutboundAuthMenu->CreateMenuBarLayoutItem(), 1, row++);
386	}
387	if (fOutboundEncryptionMenu != NULL) {
388		BMenuItem* item = fOutboundEncryptionMenu->Menu()->ItemAt(
389			info.providerInfo.ssl_smtp);
390		if (item != NULL)
391			item->SetMarked(true);
392		fOutboundEncrItemStart = item;
393
394		grid->GridLayout()->AddItem(
395			fOutboundEncryptionMenu->CreateLabelLayoutItem(), 0, row);
396		grid->GridLayout()->AddItem(
397			fOutboundEncryptionMenu->CreateMenuBarLayoutItem(), 1, row++);
398	}
399	grid->GridLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0, row);
400
401	if (!fOutboundAccount)
402		box->Hide();
403}
404
405
406ServerSettingsView::~ServerSettingsView()
407{
408	// Remove manually, as their code may be located in an add-on
409	RemoveChild(fInboundAuthMenu);
410	RemoveChild(fInboundEncryptionMenu);
411	delete fInboundAuthMenu;
412	delete fInboundEncryptionMenu;
413	unload_add_on(fImageID);
414}
415
416
417void
418ServerSettingsView::GetServerInfo(account_info& info)
419{
420	if (info.inboundType == IMAP) {
421		info.providerInfo.imap_server = fInboundNameView->Text();
422		if (fInboundEncryptionMenu != NULL) {
423			BMenuItem* item = fInboundEncryptionMenu->Menu()->FindMarked();
424			if (item != NULL) {
425				info.providerInfo.ssl_imap
426					= fInboundEncryptionMenu->Menu()->IndexOf(item);
427			}
428		}
429	} else {
430		info.providerInfo.pop_server = fInboundNameView->Text();
431		BMenuItem* item = NULL;
432		if (fInboundAuthMenu != NULL) {
433			item = fInboundAuthMenu->Menu()->FindMarked();
434			if (item != NULL) {
435				info.providerInfo.authentification_pop
436					= fInboundAuthMenu->Menu()->IndexOf(item);
437			}
438		}
439		if (fInboundEncryptionMenu != NULL) {
440			item = fInboundEncryptionMenu->Menu()->FindMarked();
441			if (item != NULL) {
442				info.providerInfo.ssl_pop
443					= fInboundEncryptionMenu->Menu()->IndexOf(item);
444			}
445		}
446	}
447	info.providerInfo.smtp_server = fOutboundNameView->Text();
448	BMenuItem* item = NULL;
449	if (fOutboundAuthMenu != NULL) {
450		item = fOutboundAuthMenu->Menu()->FindMarked();
451		if (item != NULL) {
452			info.providerInfo.authentification_smtp
453				= fOutboundAuthMenu->Menu()->IndexOf(item);
454		}
455	}
456
457	if (fOutboundEncryptionMenu != NULL) {
458		item = fOutboundEncryptionMenu->Menu()->FindMarked();
459		if (item != NULL) {
460			info.providerInfo.ssl_smtp
461				= fOutboundEncryptionMenu->Menu()->IndexOf(item);
462		}
463	}
464	_DetectMenuChanges();
465}
466
467
468void
469ServerSettingsView::_DetectMenuChanges()
470{
471	bool changed = _HasMarkedChanged(fInboundAuthMenu, fInboundAuthItemStart)
472		|| _HasMarkedChanged(fInboundEncryptionMenu, fInboundEncrItemStart)
473		|| _HasMarkedChanged(fOutboundAuthMenu, fOutboundAuthItemStart)
474		|| _HasMarkedChanged(fOutboundEncryptionMenu, fOutboundEncrItemStart);
475
476	if (changed) {
477		BMessage msg(kServerChangedMsg);
478		BMessenger messenger(NULL, Window()->Looper());
479		messenger.SendMessage(&msg);
480	}
481}
482
483
484bool
485ServerSettingsView::_HasMarkedChanged(BMenuField* field,
486	BMenuItem* originalItem)
487{
488	if (field != NULL) {
489		BMenuItem *item = field->Menu()->FindMarked();
490		if (item != originalItem)
491			return true;
492	}
493	return false;
494}
495
496
497void
498ServerSettingsView::_GetAuthEncrMenu(entry_ref protocol,
499	BMenuField*& authField, BMenuField*& sslField)
500{
501	BMailAccountSettings dummySettings;
502	BView *view = new BStringView("", "Not here!");//CreateConfigView(protocol, dummySettings.InboundSettings(),
503//		dummySettings, fImageId);
504
505	authField = (BMenuField*)view->FindView("auth_method");
506	sslField = (BMenuField*)view->FindView("flavor");
507
508	view->RemoveChild(authField);
509	view->RemoveChild(sslField);
510	delete view;
511}
512