Permalink
Browse files

* Custom 404 & 405 error pages

	* Show group members and messages count
	* Added language detection by using detectlanguage.com (Internet access
required! WARNING: Conversations fragments will be sent to
detectlanguage.com)
	* Template error fixed (major bug...)
	* Improved encryption.py and merge.py scripts
  • Loading branch information...
1 parent 1bdd932 commit 9d20e8b78e0520ce2e46141e054750de56bc150e @sch3m4 committed Nov 18, 2012
View
@@ -1,3 +1,12 @@
+WForensic (0.3)
+ * Custom 404 & 405 error pages
+ * Show group members and messages count
+ * Added language detection by using detectlanguage.com (Internet access required! WARNING: Conversations fragments will be sent to detectlanguage.com)
+ * Template error fixed (major bug...)
+ * Improved encryption.py and merge.py scripts
+
+ -- Chema Garcia <chema@safetybits.net> (XX/11/2012)
+
WForensic (0.2)
* Updated attachments thumbnails display
View
10 TODO
@@ -0,0 +1,10 @@
+[H] Add support to other devices (BlackBerry,Nokia,Windows Phone...)
+[H] Add search support (messages, contacts, phone numbers, etc.)
+[N] Detect locally the conversations language
+[L] Show emoticons
+
+--
+Legend:
+ H: Hight
+ N: Normal
+ L: Low
View
5 run.sh
@@ -21,9 +21,10 @@ echo "
== == == === ======== = == ======= ===== = ==== === == ====
=== == ==== ======== = == ======= = == = == = == == = =
==== ==== ===== ========= === ======== === = === === === ==
- ==========================================================================
+ ========================================================================== v0.3b
"
-$PYBIN wforensic/manage.py runserver
+# in order to handle 40X errors and serve static files the django server must be launched with '--insecure', you can override it by using apache/nginx/XXXX web server
+$PYBIN wforensic/manage.py runserver --insecure
exit $?
View
@@ -53,13 +53,22 @@
mode = None
+total_msg = 0
+total_contacts = 0
+
def getinfo(path):
+ global total_msg
+ global total_contacts
+
db = sqlite3.connect(path)
cur = db.cursor()
res = cur.execute("SELECT COUNT(*) FROM messages UNION ALL SELECT COUNT(DISTINCT key_remote_jid) FROM chat_list").fetchall()
cur.close()
db.close()
+
+ total_msg += res[0][0]
+ total_contacts += res[1][0]
return (res[0][0], res[1][0])
@@ -144,19 +153,25 @@ def work_dir(path, dest):
set_aes()
- for filename in os.listdir(path):
- if not os.path.isfile(path + filename):
- continue
+ aux = []
+
+ # find and store files
+ for root, dirs, files in os.walk(path):
+ for file in files:
+ aux.append(os.path.join(root,file))
+
+ filenames = sorted(aux)
- work_file(path + filename, dest )
+ for filename in filenames:
+ work_file( filename, dest )
return
-if __name__ == "__main__":
+if __name__ == "__main__":
print """
#######################################
- # WhatsApp Encryption Tool 0.3 #
+ # WhatsApp Encryption Tool 0.4 #
#-------------------------------------#
# Decrypts encrypted msgstore files #
# This tool is part of WForensic #
@@ -213,6 +228,8 @@ def work_dir(path, dest):
work_dir(args.dir, args.output)
else: # single file
work_file(args.file, args.output )
+
+ print "\n[i] Gathered %d messages from %d contacts!" % (total_msg,total_contacts)
print "\n[+] Done!\n"
sys.exit(0)
View
@@ -25,37 +25,54 @@
#
# This tool is part of WhatsApp Forensic (https://github.com/sch3m4/wforensic)
#
-# Version: 0.2b
+# Version: 0.3b
#
try:
import os
- import re
import sys
import shutil
import sqlite3
+ import fnmatch
except ImportError,e:
print "[f] Required module missing. %s" % e.args[0]
sys.exit(-1)
+COLUMNS = ['key_remote_jid','key_from_me','key_id','status','needs_push','data','timestamp','media_url','media_mime_type','media_wa_type','media_size','media_name','latitude','longitude','thumb_image','remote_resource','received_timestamp','send_timestamp','receipt_server_timestamp','receipt_device_timestamp','raw_data']
+
+tmessages = 0
+tcontacts = 0
+
def merge(path,pattern,dest):
"""
Reads from files in 'path' and dumps its contents to 'dest'
"""
+ global COLUMNS
+ global tmessages
+ global tcontacts
first = 0
output = None
mtableid = 0
-
- for filename in os.listdir(path):
- if not os.path.isfile(path + filename) or not re.match(pattern, filename):
- continue
-
+ aux = []
+
+ # find and store files
+ for root, dirs, files in os.walk(path):
+ for file in files:
+ if fnmatch.fnmatch(file,pattern):
+ aux.append(os.path.join(root,file))
+
+ filenames = sorted(aux)
+
+ for filename in filenames:
print "\n+ Merging: %s" % filename ,
sys.stdout.flush()
+
+ if os.path.isdir(dest):
+ dest += '/' + os.path.basename(filename)
if first == 0:
- shutil.copy2 (path + filename, dest)
+ shutil.copy2 (filename, dest)
first += 1
continue
elif output is None:
@@ -66,7 +83,7 @@ def merge(path,pattern,dest):
cmessages = 0
# get all remote_key_jid values from messages table
- orig = sqlite3.connect(path + filename)
+ orig = sqlite3.connect(filename)
rcursor = orig.cursor()
if mtableid == 0:
@@ -93,18 +110,33 @@ def merge(path,pattern,dest):
data = (krjid[0], mtableid)
wcursor.execute("INSERT INTO chat_list (key_remote_jid,message_table_id) VALUES (?,?)", data)
ccontacts += 1
- except:
- pass
-
+ except Exception,e:
+ print "\n[e] Error merging contact: %s" % str(e)
+
+ tcontacts += ccontacts
+
+ # check if the column 'raw_data' exists (WhatsApp versions compatibility issue)
+ try:
+ rcursor.execute("SELECT COUNT(%s) FROM messages" % COLUMNS[len(COLUMNS) - 1])
+ ncols = len(COLUMNS)
+ except sqlite3.OperationalError,e:
+ if COLUMNS[len(COLUMNS)-1] in e.message:
+ ncols = len(COLUMNS) - 1
+ else:
+ print "\n[e] Undefined error: %s" % e.message
+ continue
+
# get all messages from messages table
- rcursor.execute("SELECT key_remote_jid,key_from_me,key_id,status,needs_push,data,timestamp,media_url,media_mime_type,media_wa_type,media_size,media_name,latitude,longitude,thumb_image,remote_resource,received_timestamp,send_timestamp,receipt_server_timestamp,receipt_device_timestamp,raw_data FROM messages")
+ rcursor.execute("SELECT %s FROM messages" % ','.join(COLUMNS[:ncols]))
messages = rcursor.fetchall()
for msg in messages:
try:
- wcursor.execute("INSERT INTO messages(key_remote_jid,key_from_me,key_id,status,needs_push,data,timestamp,media_url,media_mime_type,media_wa_type,media_size,media_name,latitude,longitude,thumb_image,remote_resource,received_timestamp,send_timestamp,receipt_server_timestamp,receipt_device_timestamp,raw_data) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",msg)
+ wcursor.execute("INSERT INTO messages(%s) VALUES (%s)" % (','.join(COLUMNS[:ncols]),','.join('?' for x in range(0,ncols))),msg)
cmessages += 1
- except:
+ except Exception,e:
pass
+
+ tmessages += cmessages
output.commit()
@@ -120,13 +152,13 @@ def merge(path,pattern,dest):
if __name__ == "__main__":
print """
#######################################
- # WhatsApp Msgstore Merge Tool 0.2b #
- #-------------------------------------#
- # Merges WhatsApp message files into #
- # a single one. #
- # This tool is part of WForensic #
- # https://github.com/sch3m4/wforensic #
- #######################################
+ # WhatsApp Msgstore Merge Tool 0.3 #
+ #------------------------------------#
+ # Merges WhatsApp message files into #
+ # a single one. #
+ # This tool is part of WForensic #
+ # http://sch3m4.github.com/wforensic #
+ ######################################
"""
if len(sys.argv) != 4:
@@ -150,5 +182,5 @@ def merge(path,pattern,dest):
print "[i] Output file: %s" % sys.argv[3]
merge(sys.argv[1],sys.argv[2], sys.argv[3])
- print "\n"
+ print "\n\n[i] Merged %d contacts and %d messages!\n" % (tcontacts,tmessages)
sys.exit(0)
View
@@ -6,8 +6,8 @@
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
-# tool version
-VERSION = '0.2b'
+# project version
+VERSION = '0.3b'
# YOU CAN EDIT THE BELOW SETTINGS
##############################################################
@@ -16,6 +16,8 @@
MESSAGES_PER_PAGE = 20 # number of messages to show per page
LATEST_PEERS = 10 # number of peers to show as latest
TOP_PEERS = 10 # number of peers to show as top
+DLAPIKEY = 'demo' # detectlanguage.com API Key
+DEBUG = False # Production enviroments
##############################################################
ADMINS = (
@@ -49,9 +49,8 @@
<a href="{% url messages_media %}">Media</a> ({{theader.media}}) |
{% endif %}
{% if theader.gps > 0 %}
-<a href="{% url messages_gps %}">GPS</a> ({{theader.gps}}) |
+<a href="{% url messages_gps %}">GPS</a> ({{theader.gps}})
{% endif %}
-<a href="{% url search %}">Search</a>
<div class="ym-wrapper">
<div class="ym-wbox">
@@ -64,7 +63,7 @@
<div align="center">
<div class="footer">
<em>
- <a href="http://sch3m4.github.com/wforensic" target="_blank">WhatsApp Forensic v0.2</a><br>
+ <a href="http://sch3m4.github.com/wforensic" target="_blank">WhatsApp Forensic v0.3b</a><br>
Written by <a href="mailto:chema@safetybits.net">Chema Garc&iacute;a</a> (aka <a href="https://twitter.com/sch3m4" target="_blank">sch3m4</a>) / <a href="http://safetybits.net" target="_blank">SafetyBits</a>
</em><br/><br/>
<img src="{{ STATIC_URL }}img/powered/django.png">
View
@@ -1,27 +1,18 @@
from django.conf.urls.defaults import patterns, url
-# Uncomment the next two lines to enable the admin:
-# from django.contrib import admin
-# admin.autodiscover()
-
urlpatterns = patterns('',
- # Examples:
- # url(r'^$', 'wpf_web.views.home', name='home'),
- # url(r'^wpf_web/', include('wpf_web.foo.urls')),
-
- # Uncomment the admin/doc line below to enable admin documentation:
- # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
- # Uncomment the next line to enable the admin:
- # url(r'^admin/', include(admin.site.urls)),
url(r'^$', 'whatsapp.views.index', name='home'),
url(r'^contacts/download$', 'whatsapp.views.contacts_download', name='download_contacts'),
url(r'^contacts/$', 'whatsapp.views.contacts', name='contacts'),
url(r'^contacts/(?P<key>[\-?0-9a-zS\.\@]+)$', 'whatsapp.views.contact_profile', name='contact_profile'),
url(r'^chats/$', 'whatsapp.views.chatlist', name='chats'),
url(r'^messages/$', 'whatsapp.views.messages', name='messages'),
url(r'^messages/(?P<key>[\-?0-9a-zS\.\@]+)$', 'whatsapp.views.single_chat', name='single_chat'),
+ url(r'^messages/(?P<key>[\-?0-9a-zS\.\@]+)/detect$', 'whatsapp.views.language_detect', name='language_detect'),
url(r'^mediamsg/$', 'whatsapp.views.messages_media', name='messages_media'),
url(r'^gpsmsg/$', 'whatsapp.views.messages_gps', name='messages_gps'),
url(r'^favicon.ico$', 'django.views.generic.simple.redirect_to', {'url': '/static/img/favicon.ico'}),
)
+
+handler404 = 'whatsapp.views.error404'
+handler500 = 'whatsapp.views.error404'
@@ -2,7 +2,18 @@
{% block body %}
+<script>
+function popitup(url) {
+ newwindow=window.open(url,'name','height=500px,width=700px');
+ if (window.focus) {newwindow.focus()}
+ return false;
+}
+</script>
+
<div class="ym-g50">
+ {% ifequal PAG_TITLE "Conversation" %}
+ <a href="{% url language_detect key=peer %}?page={{ chatmessages.number }}" onclick="return popitup('{% url language_detect key=peer %}?page={{ chatmessages.number }}')">Detect Language</a><br>
+ {% endifequal %}
<div style="background: red;">
<h6><span style="color: white;">{{PAG_TITLE}}</span></h6>
</div>
@@ -39,6 +50,9 @@
<a href="{% url contact_profile key=item.key_remote_jid %}">{{item.display_name}}</a>
+ {% if item.peer %}
+ (<a href="{% url contact_profile key=item.peer_id %}">{{item.peer}}</a>)
+ {% endif %}
{% ifequal item.key_from_me 1 %}
&lArr; Me
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% block body %}
+
+<div class="ym-g50">
+ <h1>Ooooops!</h1>
+
+ <p>
+ The page you requested does not exists!!
+ </p>
+</div>
+{% endblock %}
@@ -26,6 +26,7 @@
<td><div id="messagesfrom" style="width: 480px; height: 200px; margin: 0 auto"></div></td>
</tr>
</table>
+
<div id="wa_activity" style="margin: 0 auto"></div>
<table class="bordertable" style="background: #e8ff99; ">
Oops, something went wrong.

0 comments on commit 9d20e8b

Please sign in to comment.