package org.wordpress.android.ui.accounts; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.net.ConnectivityManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; import android.util.SparseBooleanArray; import android.util.Xml; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.webkit.URLUtil; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.justsystems.hpb.pad.R; import org.apache.http.conn.HttpHostConnectException; import org.xmlpull.v1.XmlPullParser; import org.xmlrpc.android.ApiHelper; import org.xmlrpc.android.XMLRPCClient; import org.xmlrpc.android.XMLRPCException; import org.xmlrpc.android.XMLRPCFault; import org.wordpress.android.WordPress; import org.wordpress.android.WordPressDB; import org.wordpress.android.models.Blog; import org.wordpress.android.util.AlertUtil; import org.wordpress.android.util.StringUtils; import org.wordpress.android.util.Utils; public class AccountSetupActivity extends Activity implements OnClickListener { private static final String URL_WORDPRESS = "http://wordpress.com"; private static final String DEFAULT_IMAGE_SIZE = "2000"; private XMLRPCClient mClient; private String mBlogURL, mXmlrpcURL; private ProgressDialog mProgressDialog; private String mHttpuser = ""; private String mHttppassword = ""; private boolean mIsWpcom = false, mAuthOnly = false; private int mBlogCtr = 0; private ArrayList<CharSequence> mBlogNames = new ArrayList<CharSequence>(); private boolean mIsCustomURL = false; private ConnectivityManager mSystemService; private EditText mUrlEdit; private EditText mUsernameEdit; private EditText mPasswordEdit; private Button mSettingsButton; private Button mSaveButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_account); mSettingsButton = (Button) findViewById(R.id.settingsButton); mSaveButton = (Button) findViewById(R.id.save); mUrlEdit = (EditText) findViewById(R.id.url); mUsernameEdit = (EditText) findViewById(R.id.username); mPasswordEdit = (EditText) findViewById(R.id.password); ((TextView) findViewById(R.id.l_section1)).setText(getResources() .getString(R.string.account_details).toUpperCase()); mSystemService = (ConnectivityManager) getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); Bundle extras = getIntent().getExtras(); if (extras != null) { mIsWpcom = extras.getBoolean("wpcom", false); mAuthOnly = extras.getBoolean("auth-only", false); String username = extras.getString("username"); if (username != null) { mUsernameEdit.setText(username); } } if (mIsWpcom) { ((EditText) findViewById(R.id.url)).setVisibility(View.GONE); } if (mIsWpcom) { mSettingsButton.setVisibility(View.GONE); if (!mAuthOnly && WordPress.hasValidWPComCredentials(this)) { setupBlogs(); } } else { if (mAuthOnly) { Blog currentBlog = WordPress.getCurrentBlog(); if (currentBlog != null) { mUrlEdit.setText(currentBlog.getHomeURL()); mUsernameEdit.requestFocus(); } } mSettingsButton.setOnClickListener(this); } mSaveButton.setOnClickListener(this); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == R.id.settingsButton) { if (resultCode == RESULT_OK) { Bundle extras = data.getExtras(); mHttpuser = extras.getString("httpuser"); mHttppassword = extras.getString("httppassword"); } } } @Override public void onBackPressed() { setResult(RESULT_CANCELED); finish(); } private void configureAccount() { if (mIsWpcom) { mBlogURL = URL_WORDPRESS; } else { mBlogURL = mUrlEdit.getText().toString().trim(); } final String username = mUsernameEdit.getText().toString().trim(); final String password = mPasswordEdit.getText().toString().trim(); if (mBlogURL.equals("") || username.equals("") || password.equals("")) { mProgressDialog.dismiss(); AlertUtil.showAlert(AccountSetupActivity.this, R.string.required_fields, R.string.url_username_password_required); return; } // add http to the beginning of the URL if needed if (!(mBlogURL.toLowerCase().startsWith("http://")) && !(mBlogURL.toLowerCase().startsWith("https://"))) { mBlogURL = "http://" + mBlogURL; // default to http } if (!URLUtil.isValidUrl(mBlogURL)) { mProgressDialog.dismiss(); AlertUtil.showAlert(AccountSetupActivity.this, R.string.invalid_url, R.string.invalid_url_message); return; } // attempt to get the XMLRPC URL via RSD String rsdUrl = getRSDMetaTagHrefRegEx(mBlogURL); if (rsdUrl == null) { rsdUrl = getRSDMetaTagHref(mBlogURL); } if (rsdUrl != null) { mXmlrpcURL = ApiHelper.getXMLRPCUrl(rsdUrl); if (mXmlrpcURL == null) mXmlrpcURL = rsdUrl.replace("?rsd", ""); } else { mIsCustomURL = false; // try the user entered path try { mClient = new XMLRPCClient(mBlogURL, mHttpuser, mHttppassword); try { mClient.call("system.listMethods"); mXmlrpcURL = mBlogURL; mIsCustomURL = true; } catch (XMLRPCException e) { // guess the xmlrpc path String guessURL = mBlogURL; if (guessURL.substring(guessURL.length() - 1, guessURL.length()).equals("/")) { guessURL = guessURL.substring(0, guessURL.length() - 1); } guessURL += "/xmlrpc.php"; mClient = new XMLRPCClient(guessURL, mHttpuser, mHttppassword); try { mClient.call("system.listMethods"); mXmlrpcURL = guessURL; } catch (XMLRPCException ex) { } } } catch (Exception e) { } } if (mXmlrpcURL == null) { mProgressDialog.dismiss(); AlertUtil.showAlert(AccountSetupActivity.this, R.string.error, R.string.no_site_error); } else { //Valide the URL found before calling the client. Prevent a crash that can occur during the setup of self-hosted sites. try { URI.create(mXmlrpcURL); } catch (Exception e1) { mProgressDialog.dismiss(); AlertUtil.showAlert(AccountSetupActivity.this, R.string.error, R.string.no_site_error); return; } // verify settings mClient = new XMLRPCClient(mXmlrpcURL, mHttpuser, mHttppassword); XMLRPCMethod method = new XMLRPCMethod("wp.getUsersBlogs", new XMLRPCMethodCallback() { public void callFinished(Object[] result) { Blog currentBlog = WordPress.getCurrentBlog(); if (mIsWpcom) { SharedPreferences settings = PreferenceManager .getDefaultSharedPreferences(AccountSetupActivity.this); SharedPreferences.Editor editor = settings .edit(); editor.putString( WordPress.WPCOM_USERNAME_PREFERENCE, username); editor.putString( WordPress.WPCOM_PASSWORD_PREFERENCE, WordPressDB.encryptPassword(password)); editor.commit(); // fire off a request to get an access token WordPress.restClient.get("me", null, null); } if (mAuthOnly) { if (currentBlog != null) { if (mIsWpcom) { WordPress.wpDB.updateWPComCredentials( username, password); if (currentBlog != null && currentBlog.isDotcomFlag()) { currentBlog.setPassword(password); } } else { currentBlog.setPassword(password); } currentBlog.save(""); } setResult(RESULT_OK); finish(); return; } Arrays.sort(result, Utils.BlogNameComparator); final String[] blogNames = new String[result.length]; final String[] urls = new String[result.length]; final String[] homeURLs = new String[result.length]; final int[] blogIds = new int[result.length]; final boolean[] wpcoms = new boolean[result.length]; final String[] wpVersions = new String[result.length]; Map<Object, Object> contentHash = new HashMap<Object, Object>(); mBlogCtr = 0; // loop this! for (int ctr = 0; ctr < result.length; ctr++) { contentHash = (Map<Object, Object>) result[ctr]; String blogName = contentHash.get("blogName") .toString(); if (blogName.length() == 0) { blogName = contentHash.get("url") .toString(); } blogNames[mBlogCtr] = blogName; if (mIsCustomURL) urls[mBlogCtr] = mBlogURL; else urls[mBlogCtr] = contentHash.get("xmlrpc") .toString(); homeURLs[mBlogCtr] = contentHash.get("url") .toString(); blogIds[mBlogCtr] = Integer .parseInt(contentHash.get("blogid") .toString()); String blogURL = urls[mBlogCtr]; mBlogNames.add(StringUtils .unescapeHTML(blogNames[mBlogCtr] .toString())); boolean wpcomFlag = false; // check for wordpress.com if (blogURL.toLowerCase().contains( "wordpress.com")) { wpcomFlag = true; } wpcoms[mBlogCtr] = wpcomFlag; // attempt to get the software version String wpVersion = ""; if (!wpcomFlag) { Map<String, String> hPost = new HashMap<String, String>(); hPost.put("software_version", "software_version"); Object[] vParams = { 1, username, password, hPost }; Object versionResult = new Object(); try { versionResult = (Object) mClient.call( "wp.getOptions", vParams); } catch (XMLRPCException e) { } if (versionResult != null) { try { contentHash = (Map<Object, Object>) versionResult; Map<?, ?> sv = (Map<?, ?>) contentHash .get("software_version"); wpVersion = sv.get("value") .toString(); } catch (Exception e) { } } } else { wpVersion = "3.5"; } wpVersions[mBlogCtr] = wpVersion; mBlogCtr++; } // end loop mProgressDialog.dismiss(); if (mBlogCtr == 0) { String additionalText = ""; if (result.length > 0) { additionalText = getString(R.string.additional); } AlertUtil.showAlert( AccountSetupActivity.this, R.string.no_blogs_found, String.format( getString(R.string.no_blogs_message), additionalText), getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialog, int whichButton) { dialog.dismiss(); } }); } else { // take them to the blog selection screen if // there's more than one blog if (mBlogCtr > 1) { LayoutInflater inflater = (LayoutInflater) AccountSetupActivity.this .getSystemService(Context.LAYOUT_INFLATER_SERVICE); final ListView lv = (ListView) inflater .inflate( R.layout.select_blogs_list, null); lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); lv.setItemsCanFocus(false); ArrayAdapter<CharSequence> blogs = new ArrayAdapter<CharSequence>( AccountSetupActivity.this, R.layout.blogs_row, mBlogNames); lv.setAdapter(blogs); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder( AccountSetupActivity.this); dialogBuilder .setTitle(R.string.select_blogs); dialogBuilder.setView(lv); dialogBuilder .setNegativeButton( R.string.add_selected, new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialog, int whichButton) { SparseBooleanArray selectedItems = lv .getCheckedItemPositions(); for (int i = 0; i < selectedItems .size(); i++) { if (selectedItems .get(selectedItems .keyAt(i)) == true) { int rowID = selectedItems .keyAt(i); long blogID = -1; blogID = WordPress.wpDB .checkMatch( blogNames[rowID], urls[rowID], username, password); if (blogID == -1) { blogID = WordPress.wpDB .addAccount( urls[rowID], homeURLs[rowID], blogNames[rowID], username, password, mHttpuser, mHttppassword, "Above Text", false, false, DEFAULT_IMAGE_SIZE, 20, false, blogIds[rowID], wpcoms[rowID], wpVersions[rowID]); } //Set the first blog in the list to the currentBlog if (i == 0) { if (blogID >= 0) { WordPress .setCurrentBlog((int) blogID); } } } } setResult(RESULT_OK); finish(); } }); dialogBuilder .setPositiveButton( R.string.add_all, new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialog, int whichButton) { for (int i = 0; i < mBlogCtr; i++) { long blogID; blogID = WordPress.wpDB .checkMatch( blogNames[i], urls[i], username, password); if (blogID == -1) { blogID = WordPress.wpDB .addAccount( urls[i], homeURLs[i], blogNames[i], username, password, mHttpuser, mHttppassword, "Above Text", false, false, DEFAULT_IMAGE_SIZE, 5, false, blogIds[i], wpcoms[i], wpVersions[i]); } //Set the first blog in the list to the currentBlog if (i == 0) { if (blogID >= 0) { WordPress .setCurrentBlog((int) blogID); } } } setResult(RESULT_OK); finish(); } }); dialogBuilder.setCancelable(true); AlertDialog ad = dialogBuilder.create(); ad.setInverseBackgroundForced(true); ad.show(); final Button addSelected = ad .getButton(AlertDialog.BUTTON_NEGATIVE); addSelected.setEnabled(false); lv.setOnItemClickListener(new OnItemClickListener() { public void onItemClick( AdapterView<?> arg0, View arg1, int arg2, long arg3) { SparseBooleanArray selectedItems = lv .getCheckedItemPositions(); boolean isChecked = false; for (int i = 0; i < selectedItems .size(); i++) { if (selectedItems .get(selectedItems .keyAt(i)) == true) { isChecked = true; } } if (!isChecked) { addSelected.setEnabled(false); } else { addSelected.setEnabled(true); } } }); } else { long blogID; blogID = WordPress.wpDB.checkMatch( blogNames[0], urls[0], username, password); if (blogID == -1) { blogID = WordPress.wpDB.addAccount( urls[0], homeURLs[0], blogNames[0], username, password, mHttpuser, mHttppassword, "Above Text", false, false, DEFAULT_IMAGE_SIZE, 5, false, blogIds[0], wpcoms[0], wpVersions[0]); } if (blogID >= 0) { WordPress.setCurrentBlog((int) blogID); } setResult(RESULT_OK); finish(); } } } }); Object[] params = { username, password }; method.call(params); } } interface XMLRPCMethodCallback { void callFinished(Object[] result); } class XMLRPCMethod extends Thread { private String method; private Object[] params; private Handler handler; private XMLRPCMethodCallback callBack; public XMLRPCMethod(String method, XMLRPCMethodCallback callBack) { this.method = method; this.callBack = callBack; handler = new Handler(); } public void call() { call(null); } public void call(Object[] params) { this.params = params; start(); } @Override public void run() { try { final Object[] result; result = (Object[]) mClient.call(method, params); handler.post(new Runnable() { public void run() { callBack.callFinished(result); } }); } catch (final XMLRPCFault e) { handler.post(new Runnable() { public void run() { // e.printStackTrace(); mProgressDialog.dismiss(); String message = e.getMessage(); if (message.contains("code 403")) { // invalid login Thread shake = new Thread() { public void run() { Animation shake = AnimationUtils .loadAnimation( AccountSetupActivity.this, R.anim.shake); findViewById(R.id.section1).startAnimation( shake); Toast.makeText(AccountSetupActivity.this, getString(R.string.invalid_login), Toast.LENGTH_SHORT).show(); } }; runOnUiThread(shake); } else { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder( AccountSetupActivity.this); dialogBuilder .setTitle(getString(R.string.connection_error)); if (message.contains("404")) { message = getString(R.string.xmlrpc_error); } else if (message.contains("425") && mIsWpcom) {//2steps authentication enabled on this .com account dialogBuilder .setTitle(getString(R.string.info)); message = getString(R.string.account_two_step_auth_enabled); } dialogBuilder.setMessage(message); dialogBuilder.setPositiveButton( getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialog, int whichButton) { dialog.dismiss(); } }); dialogBuilder.setCancelable(true); dialogBuilder.create().show(); } } }); } catch (final XMLRPCException e) { handler.post(new Runnable() { public void run() { Throwable couse = e.getCause(); e.printStackTrace(); mProgressDialog.dismiss(); String message = e.getMessage(); if (couse instanceof HttpHostConnectException) { } else { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder( AccountSetupActivity.this); dialogBuilder .setTitle(getString(R.string.connection_error)); if (message.contains("404")) message = getString(R.string.xmlrpc_error); dialogBuilder.setMessage(message); dialogBuilder.setPositiveButton( getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialog, int whichButton) { dialog.dismiss(); } }); dialogBuilder.setCancelable(true); dialogBuilder.create().show(); } e.printStackTrace(); } }); } } } @Override public void onConfigurationChanged(Configuration newConfig) { // ignore orientation change super.onConfigurationChanged(newConfig); } private static final Pattern rsdLink = Pattern .compile( "<link\\s*?rel=\"EditURI\"\\s*?type=\"application/rsd\\+xml\"\\s*?title=\"RSD\"\\s*?href=\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); private String getRSDMetaTagHrefRegEx(String urlString) { String html = ApiHelper.getResponse(urlString); if (html != null) { Matcher matcher = rsdLink.matcher(html); if (matcher.find()) { String href = matcher.group(1); return href; } } return null; } private String getRSDMetaTagHref(String urlString) { // get the html code InputStream in = ApiHelper.getResponseStream(urlString); // parse the html and get the attribute for xmlrpc endpoint if (in != null) { XmlPullParser parser = Xml.newPullParser(); try { // auto-detect the encoding from the stream parser.setInput(in, null); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String name = null; String rel = ""; String type = ""; String href = ""; switch (eventType) { case XmlPullParser.START_TAG: name = parser.getName(); if (name.equalsIgnoreCase("link")) { for (int i = 0; i < parser.getAttributeCount(); i++) { String attrName = parser.getAttributeName(i); String attrValue = parser.getAttributeValue(i); if (attrName.equals("rel")) { rel = attrValue; } else if (attrName.equals("type")) type = attrValue; else if (attrName.equals("href")) href = attrValue; } if (rel.equals("EditURI") && type.equals("application/rsd+xml")) { return href; } // currentMessage.setLink(parser.nextText()); } break; } eventType = parser.next(); } } catch (Exception e) { e.printStackTrace(); return null; } } return null; // never found the rsd tag } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.save) { setupBlogs(); } else if (id == R.id.settingsButton) { Intent settings = new Intent(AccountSetupActivity.this, AdditionalSettingsActivity.class); settings.putExtra("httpuser", mHttpuser); settings.putExtra("httppassword", mHttppassword); startActivityForResult(settings, R.id.settingsButton); } } private void setupBlogs() { if (mSystemService.getActiveNetworkInfo() == null) { AlertUtil.showAlert(AccountSetupActivity.this, R.string.no_network_title, R.string.no_network_message); } else { mProgressDialog = ProgressDialog.show(AccountSetupActivity.this, getString(R.string.account_setup), getString(R.string.attempting_configure), true, false); if (mIsWpcom && WordPress .hasValidWPComCredentials(AccountSetupActivity.this)) { SharedPreferences settings = PreferenceManager .getDefaultSharedPreferences(AccountSetupActivity.this); mUsernameEdit.setText(settings.getString( WordPress.WPCOM_USERNAME_PREFERENCE, "")); mPasswordEdit.setText(WordPressDB.decryptPassword(settings .getString(WordPress.WPCOM_PASSWORD_PREFERENCE, ""))); } Thread action = new Thread() { public void run() { Looper.prepare(); configureAccount(); Looper.loop(); } }; action.start(); } } }