Major re-write, with new feature supporting checking balance via SMS (text message)

This commit is contained in:
moparisthebest 2013-02-26 21:42:13 -05:00
parent 93174acade
commit 343c337038
12 changed files with 489 additions and 254 deletions

View File

@ -19,22 +19,29 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.moparisthebest.pageplus" package="org.moparisthebest.pageplus"
android:versionName="0.4" android:versionCode="5"> android:versionName="0.5" android:versionCode="6">
<application android:label="@string/app_name" android:icon="@drawable/icon"> <application android:label="@string/app_name" android:icon="@drawable/icon">
<activity android:name=".Main" <activity android:name=".Main"
android:label="@string/app_name" android:label="@string/app_name"
android:configChanges="orientation" android:configChanges="orientation"
android:launchMode="singleTask"
> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name=".SMSReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
</application> </application>
<uses-sdk android:minSdkVersion="7"/> <uses-sdk android:minSdkVersion="7"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission> <uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
</manifest> </manifest>

View File

@ -58,6 +58,10 @@
android:layout_below="@+id/save" android:id="@+id/pp_only" android:layout_height="wrap_content" android:layout_below="@+id/save" android:id="@+id/pp_only" android:layout_height="wrap_content"
android:layout_toRightOf="@+id/TextView01"></CheckBox> android:layout_toRightOf="@+id/TextView01"></CheckBox>
<Button android:layout_toRightOf="@+id/TextView01" android:id="@+id/sms" android:layout_below="@+id/pp_only"
android:text="@string/sms_grab" android:layout_height="wrap_content"
android:layout_width="wrap_content"></Button>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@ -19,12 +19,14 @@
<resources> <resources>
<string name="app_name">Page Plus Balance</string> <string name="app_name">Page Plus Balance</string>
<string name="plan_data">No Data Yet\nBalance:\nPlan:\nMinutes:\nTexts:\nData:\n</string> <string name="plan_data">Balance:\nPlan:\nMinutes:\nSMS:\nData:\nLast Updated: Never\n</string>
<string name="username">Username:</string> <string name="username">Username:</string>
<string name="password">Password:</string> <string name="password">Password:</string>
<string name="phone">Phone name:</string> <string name="phone">Phone name:</string>
<string name="button">Save / Grab Data</string> <string name="button">Save / Grab Data via website</string>
<string name="pp_only">Use only pageplus\' website (uses significantly more data)</string> <string name="pp_only">Use only pagepluscellular.com (uses significantly more data)</string>
<string name="sms_grab">Grab Data via SMS</string>
</resources> </resources>

View File

@ -1,67 +0,0 @@
/*
* PagePlusBalance retrieves your balance from PagePlusCellular.com, currently for android phones.
* Copyright (C) 2013 Travis Burtrum (moparisthebest)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.moparisthebest.pageplus;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import org.moparisthebest.pageplus.plugins.PPInfo;
public class AndroidPPInfo {
private Context parent;
public AndroidPPInfo(Context parent) {
this.parent = parent;
}
public void grabData() throws Exception {
NetworkInfo netInfo = (NetworkInfo) ((ConnectivityManager) parent
.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (netInfo == null || !netInfo.isConnected() || netInfo.isRoaming())
return;
SharedPreferences settings = parent.getSharedPreferences(Main.PREFS_NAME, 0);
PPInfo pp;
if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE && !settings.getBoolean(Main.PP_ONLY, false))
pp = (PPInfo) new org.moparisthebest.pageplus.plugins.PPServer();
else // we are connected with wifi?
pp = (PPInfo) new org.moparisthebest.pageplus.plugins.PagePlusHTTP();
String user = settings.getString(Main.USER, Main.EMPTY);
if (user.equals(Main.EMPTY))
throw new Exception("Username cannot be empty.");
String pass = settings.getString(Main.PASS, Main.EMPTY);
if (pass.equals(Main.EMPTY))
throw new Exception("Password cannot be empty.");
String phone = settings.getString(Main.PHONE, Main.EMPTY);
// phone could be empty, I guess...
pp.grabData(user, pass, phone);
SharedPreferences.Editor editor = settings.edit();
for (int x = 0; x < pp.names.length; ++x)
editor.putString(pp.names[x], pp.info[x]);
editor.commit();
}
}

View File

@ -20,10 +20,11 @@ package org.moparisthebest.pageplus;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.*;
import android.content.DialogInterface; import android.net.ConnectivityManager;
import android.content.SharedPreferences; import android.net.NetworkInfo;
import android.os.Bundle; import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -31,21 +32,36 @@ import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import org.moparisthebest.pageplus.dto.Balance;
import java.util.Date; import org.moparisthebest.pageplus.plugins.PPInfo;
public class Main extends Activity { public class Main extends Activity {
public static final String PREFS_NAME = "page_plus", USER = "user", PASS = "pass", public static final String PREFS_NAME = "page_plus", USER = "user", PASS = "pass",
PHONE = "phone", ERROR = "error", DATE = "date", PP_ONLY = "pp_only", EMPTY = ""; PHONE = "phone", PP_ONLY = "pp_only", EMPTY = "";
public static final String PP_BAL_RECEIVED_ACTION = "PP_BAL_RECEIVED_ACTION";
public static final String BALANCE = "BALANCE_COMPACT";
private Button closeButton;
private EditText username, password, phone; private EditText username, password, phone;
private CheckBox pp_only; private CheckBox pp_only;
private TextView plan_data; private TextView plan_data;
private SharedPreferences settings; private SharedPreferences settings;
private Context thisContext; private Context thisContext;
private ProgressDialog pd;
private IntentFilter intentFilter;
private BroadcastReceiver intentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
populateData(intent.getExtras().getString(BALANCE));
if (pd != null && pd.isShowing())
pd.dismiss();
}
};
/** /**
* Called when the activity is first created. * Called when the activity is first created.
*/ */
@ -66,7 +82,7 @@ public class Main extends Activity {
this.settings = getSharedPreferences(PREFS_NAME, 0); this.settings = getSharedPreferences(PREFS_NAME, 0);
populate_data(); populateData();
this.username.setText(this.settings.getString(USER, EMPTY)); this.username.setText(this.settings.getString(USER, EMPTY));
this.password.setText(this.settings.getString(PASS, EMPTY)); this.password.setText(this.settings.getString(PASS, EMPTY));
@ -74,8 +90,21 @@ public class Main extends Activity {
this.pp_only.setChecked(this.settings.getBoolean(PP_ONLY, false)); this.pp_only.setChecked(this.settings.getBoolean(PP_ONLY, false));
this.closeButton = (Button) this.findViewById(R.id.save); this.pd = new ProgressDialog(thisContext);
this.closeButton.setOnClickListener(new OnClickListener() { pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setIndeterminate(true);
/*
pd.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
populateData();
}
});*/
intentFilter = new IntentFilter();
intentFilter.addAction(PP_BAL_RECEIVED_ACTION);
Button saveButton = (Button) this.findViewById(R.id.save);
saveButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
final SharedPreferences.Editor editor = settings.edit(); final SharedPreferences.Editor editor = settings.edit();
@ -90,37 +119,44 @@ public class Main extends Activity {
editor.commit(); editor.commit();
try { try {
final ProgressDialog pd = new ProgressDialog(thisContext);
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setTitle("Grabbing data..."); pd.setTitle("Grabbing data...");
pd.setMessage("be patient"); pd.setMessage("be patient");
pd.setIndeterminate(true);
pd.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
populate_data();
}
});
pd.show(); pd.show();
// final Handler pdHandler = new Handler(); // final Handler pdHandler = new Handler();
new Thread(new Runnable() { new Thread(new Runnable() {
public void run() { public void run() {
Balance balance = getBalance();
try { Intent broadcastIntent = new Intent();
new AndroidPPInfo(thisContext).grabData(); broadcastIntent.setAction(Main.PP_BAL_RECEIVED_ACTION);
editor.putString(DATE, new Date().toLocaleString()); broadcastIntent.putExtra(Main.BALANCE, balance == null ? null : balance.compactFormat());
editor.remove(ERROR); thisContext.sendBroadcast(broadcastIntent);
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
editor.putString(ERROR, e.getMessage());
}
editor.commit();
pd.dismiss();
} }
}).start(); }).start();
} catch (Exception e) {
// grabbing data failed
// currently we just ignore this and go with the last data
//e.printStackTrace();
Log.i("PagePlus", Log.getStackTraceString(e));
}
}
});
Button smsButton = (Button) this.findViewById(R.id.sms);
smsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
pd.setTitle("Sending SMS...");
pd.setMessage("and waiting for reply, be patient");
pd.show();
// final Handler pdHandler = new Handler();
new Thread(new Runnable() {
public void run() {
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage("7243", null, "BAL", null, null);
}
}).start();
} catch (Exception e) { } catch (Exception e) {
// grabbing data failed // grabbing data failed
// currently we just ignore this and go with the last data // currently we just ignore this and go with the last data
@ -133,6 +169,46 @@ public class Main extends Activity {
} }
@Override
protected void onResume() {
registerReceiver(intentReceiver, intentFilter);
super.onResume();
}
@Override
protected void onPause() {
unregisterReceiver(intentReceiver);
super.onPause();
}
private Balance getBalance() {
NetworkInfo netInfo = ((ConnectivityManager) thisContext.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (netInfo == null || !netInfo.isConnected() || netInfo.isRoaming())
return null;
SharedPreferences settings = thisContext.getSharedPreferences(Main.PREFS_NAME, 0);
String user = settings.getString(Main.USER, Main.EMPTY);
if (user.equals(Main.EMPTY))
return new Balance().setError("Username cannot be empty.");
String pass = settings.getString(Main.PASS, Main.EMPTY);
if (pass.equals(Main.EMPTY))
return new Balance().setError("Password cannot be empty.");
String phone = settings.getString(Main.PHONE, Main.EMPTY);
// phone could be empty, I guess...
PPInfo pp;
if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE && !settings.getBoolean(Main.PP_ONLY, false))
pp = new org.moparisthebest.pageplus.plugins.PPServer();
else // we are connected with wifi
pp = new org.moparisthebest.pageplus.plugins.PagePlusHTTP();
return pp.grabData(user, pass, phone);
}
public static void trim_input(EditText et) { public static void trim_input(EditText et) {
trim_input(et, false); trim_input(et, false);
} }
@ -141,15 +217,25 @@ public class Main extends Activity {
et.setText(toLowerCase ? et.getText().toString().trim().toLowerCase() : et.getText().toString().trim()); et.setText(toLowerCase ? et.getText().toString().trim().toLowerCase() : et.getText().toString().trim());
} }
private void populate_data() { private synchronized void populateData(String balanceCompact) {
String data = ""; SharedPreferences.Editor editor = settings.edit();
settings = getSharedPreferences(PREFS_NAME, 0); Balance balance = new Balance(balanceCompact);
// if there was an error, put that first: if (balance.error != null) {
if (settings.contains(ERROR)) // then there was an error and we want to save some info from the last successful attempt
data += "Error: " + settings.getString(ERROR, EMPTY) + "\nPrevious data:\n"; balanceCompact = settings.getString(BALANCE, null);
for (int x = 0; x < org.moparisthebest.pageplus.plugins.PPInfo.names.length; ++x) if (balanceCompact != null)
data += org.moparisthebest.pageplus.plugins.PPInfo.names[x] + ": " + settings.getString(org.moparisthebest.pageplus.plugins.PPInfo.names[x], EMPTY) + "\n"; balanceCompact = balance.copyFrom(new Balance(balanceCompact)).compactFormat();
data += "Last Updated: " + settings.getString(DATE, EMPTY) + "\n"; }
plan_data.setText(data); editor.putString(BALANCE, balanceCompact);
editor.commit();
populateData(balance);
}
private synchronized void populateData(Balance balance) {
plan_data.setText(balance.toString());
}
private synchronized void populateData() {
populateData(new Balance(settings.getString(BALANCE, EMPTY)));
} }
} }

View File

@ -0,0 +1,77 @@
package org.moparisthebest.pageplus;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import org.moparisthebest.pageplus.dto.Balance;
public class SMSReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle == null)
return;
Object[] pdus = (Object[]) bundle.get("pdus");
for (int i = 0; i < pdus.length; ++i) {
SmsMessage msg = SmsMessage.createFromPdu((byte[]) pdus[i]);
if (!"7243".equals(msg.getOriginatingAddress()))
continue;
// it's a message from Page Plus, but is it a balance message?
String message = msg.getMessageBody();
if (message == null || !message.startsWith("Balance:"))
continue;
// it IS a balance message, get to parsing...
try {
// split string by newlines
final String[] msgLines = message.split("\n");
final Balance balance = new Balance();
final String regexStrip = "^[^:]*: ";
// get balance
balance.info[0] = String.format("%s (expires %s)",
msgLines[0].replaceFirst(regexStrip, ""),
msgLines[1].replaceFirst(regexStrip, ""));
// get plan
balance.info[1] = String.format("%s (expires %s)",
msgLines[2],
msgLines[3].replaceFirst(regexStrip, ""));
// get minutes
balance.info[2] = msgLines[4].replaceFirst(regexStrip, "");
// get text
balance.info[3] = msgLines[5].replaceFirst(regexStrip, "");
// get data
balance.info[4] = msgLines[6].replaceFirst(regexStrip, "");
// test
//android.widget.Toast.makeText(context, balance.toString(), android.widget.Toast.LENGTH_SHORT).show();
//---send a broadcast intent to update the SMS received in the activity---
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(Main.PP_BAL_RECEIVED_ACTION);
broadcastIntent.putExtra(Main.BALANCE, balance.success().compactFormat());
context.sendBroadcast(broadcastIntent);
/*
// now save them
SharedPreferences settings = Main.getMainContext().getSharedPreferences(Main.PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
for (int x = 0; x < PPInfo.names.length; ++x)
editor.putString(PPInfo.names[x], info[x]);
editor.putString(Main.DATE, new Date().toLocaleString());
editor.remove(Main.ERROR);
editor.commit();
Main.getMainContext().populateData();
*/
} catch (Exception e) {
//e.printStackTrace();
//editor.putString(ERROR, e.getMessage());
}
//editor.commit();
}
}
}

View File

@ -0,0 +1,90 @@
package org.moparisthebest.pageplus.dto;
import java.util.Date;
public class Balance {
private static final String[] names = new String[]{"Balance", "Plan", "Minutes", "SMS", "Data"};
private static String compactFormatDelim = "\n";
public final String[] info = new String[names.length];
public String error = null;
public Date successDate = null;
public Balance() {
}
public Balance(final String compactFormat) {
if (compactFormat == null || compactFormat.isEmpty())
return;
String[] split = compactFormat.split(compactFormatDelim);
//System.out.println("compactFormat: " + compactFormat);
//System.out.println("split: " + java.util.Arrays.toString(split));
int x = 0;
for (; x < info.length; ++x)
info[x] = nullForEmpty(split[x]);
if (x < split.length)
error = nullForEmpty(split[x++]);
if (x < split.length)
try {
String date = nullForEmpty(split[x++]);
if (date != null)
successDate = new Date(Long.parseLong(date));
} catch (Throwable e) {
// do nothing
}
}
public String compactFormat() {
StringBuilder sb = new StringBuilder();
for (String str : info) {
if (str != null)
sb.append(str);
sb.append(compactFormatDelim);
}
if (error != null)
sb.append(error);
sb.append(compactFormatDelim);
if (successDate != null)
sb.append(successDate.getTime());
return sb.toString();
}
private static String nullForEmpty(String s) {
return (s == null || s.isEmpty()) ? null : s;
}
public Balance copyFrom(Balance lastSuccessful) {
System.arraycopy(lastSuccessful.info, 0, this.info, 0, this.info.length);
this.successDate = lastSuccessful.successDate;
return this;
}
public Balance setError(String error) {
this.error = error;
successDate = null;
return this;
}
public Balance success() {
successDate = new Date();
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (error != null)
sb.append("Error: ").append(error).append("\nPrevious data:\n");
for (int x = 0; x < info.length; ++x) {
sb.append(names[x]).append(": ");
if (info[x] != null)
sb.append(info[x]);
sb.append("\n");
}
sb.append("Last Updated: ");
if (successDate != null)
sb.append(successDate.toString());
sb.append("\n");
return sb.toString();
}
}

View File

@ -18,15 +18,14 @@
package org.moparisthebest.pageplus.plugins; package org.moparisthebest.pageplus.plugins;
import org.moparisthebest.pageplus.dto.Balance;
public abstract class PPInfo { public abstract class PPInfo {
public static final String[] names = new String[]{"Balance", "Plan", "Minutes", "SMS", "Data"}; public Balance grabData(String[] userPassPhone) {
public String[] info; return this.grabData(userPassPhone[0], userPassPhone[1], userPassPhone[2]);
public void grabData(String[] userPassPhone) throws Exception {
this.grabData(userPassPhone[0], userPassPhone[1], userPassPhone[2]);
} }
public abstract void grabData(String user, String pass, String phone) throws Exception; public abstract Balance grabData(String user, String pass, String phone);
} }

View File

@ -18,10 +18,13 @@
package org.moparisthebest.pageplus.plugins; package org.moparisthebest.pageplus.plugins;
import org.moparisthebest.pageplus.dto.Balance;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import java.io.*; import java.io.*;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.Date;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
public class PPServer extends PPInfo { public class PPServer extends PPInfo {
@ -40,19 +43,22 @@ public class PPServer extends PPInfo {
*/ */
public static final boolean useGzip = false; public static final boolean useGzip = false;
public static final boolean useSSL = false; public static final boolean useSSL = false;
// 66.55.93.152 is android.moparisthebest.org, it saves a little data not having to do the DNS lookup // this is android.moparisthebest.org, it saves a little data not having to do the DNS lookup
private String address = "66.55.93.152"; public static final String address = "66.55.93.152";
private int port = 1337; //public static final String address = "127.0.0.1"; // for testing
public static final int port = 1337;
public static final int sslPort = 1338;
@Override @Override
public void grabData(String user, String pass, String phone) throws Exception { public Balance grabData(String user, String pass, String phone) {
//System.setProperty("javax.net.ssl.trustStore", "/home/mopar/workspace/PagePlusClient/pageplus"); //System.setProperty("javax.net.ssl.trustStore", "/home/mopar/workspace/PagePlusClient/pageplus");
//System.setProperty("javax.net.ssl.trustStorePassword", "dvorak"); //System.setProperty("javax.net.ssl.trustStorePassword", "dvorak");
//System.setProperty("javax.net.debug", "ssl"); //System.setProperty("javax.net.debug", "ssl");
try {
Socket s; Socket s;
if (useSSL) if (useSSL)
s = SSLSocketFactory.getDefault().createSocket(InetAddress.getByName(address), port + 1); s = SSLSocketFactory.getDefault().createSocket(InetAddress.getByName(address), sslPort);
else else
s = new Socket(InetAddress.getByName(address), port); s = new Socket(InetAddress.getByName(address), port);
@ -62,6 +68,7 @@ public class PPServer extends PPInfo {
os = new java.util.zip.GZIPOutputStream(os); os = new java.util.zip.GZIPOutputStream(os);
is = new java.util.zip.GZIPInputStream(is); is = new java.util.zip.GZIPInputStream(is);
} }
/* DataOutputStream out = new DataOutputStream(os); /* DataOutputStream out = new DataOutputStream(os);
DataInputStream in = new DataInputStream(is); DataInputStream in = new DataInputStream(is);
out.writeUTF(user); out.writeUTF(user);
@ -69,7 +76,6 @@ public class PPServer extends PPInfo {
out.writeUTF(phone); out.writeUTF(phone);
*/ */
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
out.write(user + "\n"); out.write(user + "\n");
out.write(pass + "\n"); out.write(pass + "\n");
out.write(phone + "\n"); out.write(phone + "\n");
@ -78,16 +84,21 @@ public class PPServer extends PPInfo {
if (useGzip) if (useGzip)
((GZIPOutputStream) os).finish(); ((GZIPOutputStream) os).finish();
info = new String[5]; ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int x = 0; x < info.length; ++x) { byte[] buffer = new byte[1024];
info[x] = in.readLine(); int length = 0;
// first line will be 'e' if there is an error, next line is the error message while ((length = is.read(buffer)) != -1)
if (x == 0 && info[x].equals("e")) baos.write(buffer, 0, length);
throw new Exception(in.readLine()); Balance ret = new Balance(new String(baos.toByteArray()));
//info[x] = in.readUTF(); // one little trick, the server doesn't send the date because it might not be the same as OUR date, so if
// error is null, set the date to a current one
if (ret.error == null)
ret.successDate = new Date();
return ret;
} catch (Throwable e) {
//e.printStackTrace();
return new Balance().setError(e.getMessage());
} }
s.close();
} }
} }

View File

@ -23,6 +23,7 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.moparisthebest.pageplus.dto.Balance;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -41,11 +42,13 @@ public class PagePlusHTTP extends PPInfo {
// private static final String userAgent = // private static final String userAgent =
// "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3"; // "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3";
private static final String userAgent = "Mozilla/5.0 PagePlusAndroidWidget/0.4"; private static final String userAgent = "Mozilla/5.0 PagePlusAndroidWidget/0.5";
@Override @Override
public void grabData(String user, String pass, String phone) throws Exception { public Balance grabData(String user, String pass, String phone) {
Balance ret = new Balance();
try {
Connection.Response res = Jsoup.connect("https://www.pagepluscellular.com/login/") Connection.Response res = Jsoup.connect("https://www.pagepluscellular.com/login/")
.userAgent(userAgent) .userAgent(userAgent)
.data("username", user, "password", pass, .data("username", user, "password", pass,
@ -69,7 +72,7 @@ public class PagePlusHTTP extends PPInfo {
//Elements phones = doc.select("select#ContentPlaceHolderDefault_mainContentArea_Item2_My Account Summary_5_Registred1_DrpAccounts").first(); //Elements phones = doc.select("select#ContentPlaceHolderDefault_mainContentArea_Item2_My Account Summary_5_Registred1_DrpAccounts").first();
final Element select = doc.select("select").first(); final Element select = doc.select("select").first();
if (select == null) if (select == null)
throw new Exception("Your username or password is invalid."); return ret.setError("Your username or password is invalid.");
final Elements phones = select.select("option"); final Elements phones = select.select("option");
final Map<String, String> phoneToId = new HashMap<String, String>(phones.size()); final Map<String, String> phoneToId = new HashMap<String, String>(phones.size());
@ -82,42 +85,44 @@ public class PagePlusHTTP extends PPInfo {
if (selectedPhone == null && "selected".equals(phoneOption.attr("selected"))) if (selectedPhone == null && "selected".equals(phoneOption.attr("selected")))
selectedPhone = phoneName; selectedPhone = phoneName;
} }
System.out.printf("selectedPhone: '%s', registered phones: %s\n", selectedPhone, phoneToId); //System.out.printf("selectedPhone: '%s', registered phones: %s\n", selectedPhone, phoneToId);
if (!phone.equals(selectedPhone)) { if (!phone.equals(selectedPhone) && phoneToId.size() > 1) {
final String id = phoneToId.get(phone); final String id = phoneToId.get(phone);
if (id != null) { if (id != null) {
// we need to request the page with OUR info on it // we need to request the page with OUR info on it
Map<String, String> inputs = getFormFields(doc); Map<String, String> inputs = getFormFields(doc);
inputs.put("ctl00$ctl00$ctl00$ContentPlaceHolderDefault$mainContentArea$Item2$My Account Summary_5$Registred1$DrpAccounts", id); inputs.put("ctl00$ctl00$ctl00$ContentPlaceHolderDefault$mainContentArea$Item2$My Account Summary_5$Registred1$DrpAccounts", id);
System.out.println("inputs: " + inputs); //System.out.println("inputs: " + inputs);
doc = Jsoup.connect("https://customer.pagepluscellular.com/my-account/my-account-summary.aspx").cookies(res.cookies()) doc = Jsoup.connect("https://customer.pagepluscellular.com/my-account/my-account-summary.aspx").cookies(res.cookies())
.data(inputs).method(Connection.Method.POST).timeout(10 * 1000) .data(inputs).method(Connection.Method.POST).timeout(10 * 1000)
.execute().parse(); .execute().parse();
} else { } else {
// phone not found, print error messages // phone not found, print error messages
throw new Exception("Phone not found! Registered phones: " + phoneToId.keySet()); return ret.setError("Phone not found! Registered phones: " + phoneToId.keySet());
} }
} }
//System.out.println("doc: " + doc.html()); //System.out.println("doc: " + doc.html());
info = new String[names.length];
int index = -1; int index = -1;
try { try {
info[++index] = doc.select("span[id=ContentPlaceHolderDefault_mainContentArea_Item2_My Account Summary_5_Registred1_lblBalance]").first().text(); ret.info[++index] = doc.select("span[id=ContentPlaceHolderDefault_mainContentArea_Item2_My Account Summary_5_Registred1_lblBalance]").first().text();
} catch (Throwable e) { } catch (Throwable e) {
//e.printStackTrace(); //e.printStackTrace();
} }
try { try {
final Element balance = doc.select("div[id=ContentPlaceHolderDefault_mainContentArea_Item2_My Account Summary_5_Registred1_divBundleDetails]").first(); final Element balance = doc.select("div[id=ContentPlaceHolderDefault_mainContentArea_Item2_My Account Summary_5_Registred1_divBundleDetails]").first();
info[++index] = balance.select("tr.tableHeading").first().text(); ret.info[++index] = balance.select("tr.tableHeading").first().text();
for (Element row : balance.select("tr.odd").first().select("td")) for (Element row : balance.select("tr.odd").first().select("td"))
info[++index] = row.text(); ret.info[++index] = row.text();
//System.out.println("balance: " + balance); //System.out.println("balance: " + balance);
} catch (Throwable e) { } catch (Throwable e) {
//e.printStackTrace(); //e.printStackTrace();
} }
} catch (Throwable e) {
return ret.setError(e.getMessage());
}
return ret.success();
} }
public static Map<String, String> getFormFields(Element form) { public static Map<String, String> getFormFields(Element form) {

View File

@ -18,14 +18,19 @@
package org.moparisthebest.pageplus.server; package org.moparisthebest.pageplus.server;
import org.moparisthebest.pageplus.dto.Balance;
import org.moparisthebest.pageplus.plugins.PPInfo; import org.moparisthebest.pageplus.plugins.PPInfo;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import java.io.*; import java.io.*;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import static org.moparisthebest.pageplus.plugins.PPServer.*;
public class Main extends Thread { public class Main extends Thread {
public static void main(String[] args) { public static void main(String[] args) {
@ -34,28 +39,31 @@ public class Main extends Thread {
// System.setProperty("javax.net.ssl.keyStorePassword", "dvorak"); // System.setProperty("javax.net.ssl.keyStorePassword", "dvorak");
// System.setProperty("javax.net.debug", "ssl"); // System.setProperty("javax.net.debug", "ssl");
String address = "66.55.93.152"; if (useSSL)
int port = 1337;
final ServerSocket sSocket;
//final SSLServerSocket sslSocket;
try { try {
sSocket = new ServerSocket(port, 0, InetAddress.getByName(address)); final SSLServerSocket sslSocket = (SSLServerSocket)
System.out.println("Listening on " + address + ":" + port); SSLServerSocketFactory.getDefault().createServerSocket(sslPort, 0,
InetAddress.getByName(address));
System.out.println("Listening via SSL on " + address + ":" + sslPort);
// start new thread for SSL accepting
new Thread() {
public void run() {
while (true)
try {
new Main(sslSocket.accept());
} catch (IOException e) {
// e.printStackTrace();
}
}
}.start();
} catch (Exception e) { } catch (Exception e) {
System.out.println("Fatal Error:" + e.getMessage()); System.out.println("Fatal Error listening on SSL:" + e.getMessage());
return; return;
} }
/*
* try { sslSocket = (SSLServerSocket) try {
* SSLServerSocketFactory.getDefault().createServerSocket(++port, 0, final ServerSocket sSocket = new ServerSocket(port, 0, InetAddress.getByName(address));
* InetAddress.getByName(address)); System.out.println("Listening on " + address + ":" + port);
* System.out.println("Listening via SSL on " + address + ":" + port); }
* catch (Exception e) { System.out.println("Fatal Error:" +
* e.getMessage()); return; } // start SSL accepting thread new
* Thread(){ public void run() { while (true) try { new
* Main(sslSocket.accept()); } catch (IOException e) { //
* e.printStackTrace(); } } }.start();
*/
// use this thread to accept from regular socket // use this thread to accept from regular socket
while (true) while (true)
try { try {
@ -63,12 +71,16 @@ public class Main extends Thread {
} catch (IOException e) { } catch (IOException e) {
// e.printStackTrace(); // e.printStackTrace();
} }
} catch (Exception e) {
System.out.println("Fatal Error:" + e.getMessage());
return;
}
} }
private Socket s; private Socket s;
public Main(Socket s) { public Main(Socket s) {
System.out.println(s.getInetAddress().getHostName() + " connected to server.\n"); System.out.println(s.getInetAddress().getHostName() + " connected to server.");
this.s = s; this.s = s;
this.start(); this.start();
} }
@ -78,7 +90,7 @@ public class Main extends Thread {
try { try {
OutputStream os = s.getOutputStream(); OutputStream os = s.getOutputStream();
InputStream is = s.getInputStream(); InputStream is = s.getInputStream();
if (org.moparisthebest.pageplus.plugins.PPServer.useGzip) { if (useGzip) {
os = new java.util.zip.GZIPOutputStream(os); os = new java.util.zip.GZIPOutputStream(os);
is = new java.util.zip.GZIPInputStream(is); is = new java.util.zip.GZIPInputStream(is);
} }
@ -92,22 +104,24 @@ public class Main extends Thread {
userPassPhone[x] = in.readLine(); userPassPhone[x] = in.readLine();
// userPassPhone[x] = in.readUTF(); // userPassPhone[x] = in.readUTF();
// for(String st: userPassPhone) System.out.println("st: "+st); // for(String st: userPassPhone) System.out.println("st: "+st);
PPInfo pp = (PPInfo) new org.moparisthebest.pageplus.plugins.PagePlusHTTP(); PPInfo pp = new org.moparisthebest.pageplus.plugins.PagePlusHTTP();
Balance balance;
try { try {
pp.grabData(userPassPhone); balance = pp.grabData(userPassPhone);
} catch (Exception e) { // never send the date, wastes bandwidth and we discard it at client anyhow
balance.successDate = null;
} catch (Throwable e) {
// fudge this to send back exception // fudge this to send back exception
pp.info = new String[]{"e", e.getMessage()}; balance = new Balance().setError(e.getMessage());
e.printStackTrace();
} }
for (String st : pp.info) out.write(balance.compactFormat());
out.write(st + "\n");
// out.writeUTF(st); // out.writeUTF(st);
out.flush(); out.flush();
if (org.moparisthebest.pageplus.plugins.PPServer.useGzip) if (useGzip)
((GZIPOutputStream) os).finish(); ((GZIPOutputStream) os).finish();
s.close(); s.close();
} catch (Exception e) { } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
} }

View File

@ -18,8 +18,11 @@
package org.moparisthebest.pageplus; package org.moparisthebest.pageplus;
import org.moparisthebest.pageplus.dto.Balance;
import org.moparisthebest.pageplus.plugins.PPInfo; import org.moparisthebest.pageplus.plugins.PPInfo;
import java.util.Arrays;
public class PagePlusClient { public class PagePlusClient {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
@ -28,14 +31,18 @@ public class PagePlusClient {
return; return;
} }
PPInfo pp; PPInfo pp;
pp = new org.moparisthebest.pageplus.plugins.PagePlusHTTP(); //pp = new org.moparisthebest.pageplus.plugins.PagePlusHTTP();
//pp = new org.moparisthebest.pageplus.plugins.PPServer(); pp = new org.moparisthebest.pageplus.plugins.PPServer();
pp.grabData(args); Balance b = pp.grabData(args);
System.out.println("original balance:");
if (pp.info != null) System.out.println(b);
for (int x = 0; x < PPInfo.names.length; ++x) System.out.println("compact balance:");
System.out.println(PPInfo.names[x] + ": " + pp.info[x]); String compactBalance = b.compactFormat();
System.out.println(compactBalance);
System.out.println(Arrays.toString(compactBalance.split("\n")));
System.out.println("balance from compact balance:");
System.out.println(new Balance(compactBalance));
} }
} }