├── stego
├── __init__.py
├── decode
├── embed
├── bitmap.h
├── stego.py
├── bitmap.c
├── jinclude.h
├── decode.c
├── cderror.h
├── cdjpeg.h
└── embed.c
├── api_access
├── __init__.py
├── insta_access.py
└── InstagramAPI.py
├── img_prep
├── __init__.py
├── .jpg_prepped.jpg
├── get_qtables_from_me.jpg
└── img_prep.py
├── examples
├── message.txt
├── biking.jpg
├── piercing.jpg
├── biking_prepped.jpg
├── piercing_prepped.jpg
├── biking_prepped_encoded.jpg
└── piercing_prepped_encoded.jpg
├── .gitignore
├── LICENSE.md
├── instegogram.py
└── README.md
/stego/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api_access/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img_prep/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/message.txt:
--------------------------------------------------------------------------------
1 | test text!
2 | more text
--------------------------------------------------------------------------------
/stego/decode:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/stego/decode
--------------------------------------------------------------------------------
/stego/embed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/stego/embed
--------------------------------------------------------------------------------
/examples/biking.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/biking.jpg
--------------------------------------------------------------------------------
/examples/piercing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/piercing.jpg
--------------------------------------------------------------------------------
/img_prep/.jpg_prepped.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/img_prep/.jpg_prepped.jpg
--------------------------------------------------------------------------------
/examples/biking_prepped.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/biking_prepped.jpg
--------------------------------------------------------------------------------
/examples/piercing_prepped.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/piercing_prepped.jpg
--------------------------------------------------------------------------------
/img_prep/get_qtables_from_me.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/img_prep/get_qtables_from_me.jpg
--------------------------------------------------------------------------------
/examples/biking_prepped_encoded.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/biking_prepped_encoded.jpg
--------------------------------------------------------------------------------
/examples/piercing_prepped_encoded.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/piercing_prepped_encoded.jpg
--------------------------------------------------------------------------------
/stego/bitmap.h:
--------------------------------------------------------------------------------
1 | #ifndef _BITMAP_H
2 | #define _BITMAP_H
3 |
4 | #define BIT (8*sizeof(byte))
5 | #define BITMAP_NOTFOUND -1
6 |
7 | typedef enum{false=0, true} bool;
8 | typedef unsigned char byte;
9 |
10 | bool bitmapGet (const byte *, int);
11 | void bitmapSet (byte *, int);
12 | void bitmapFlip (byte *, int);
13 | void bitmapClear (byte *, int);
14 | void bitmapAssign(byte *, int, bool);
15 | int bitmapSearch(byte *, bool, int, int);
16 |
17 | bool get (byte, byte);
18 | void set (byte *, byte);
19 | void clear(byte *, byte);
20 | void assign(byte*, byte, byte);
21 | void xor(byte *, byte);
22 |
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/stego/stego.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import os
3 |
4 |
5 | def encode(filepath, message):
6 | """ Encodes an image specified by filepath with a message in a text file """
7 | print 'Encoded message: '
8 | with open(message, 'r') as f:
9 | message_txt = ''.join(f.readlines())
10 | print message_txt
11 | encoded_filepath = filepath[:-4] + '_encoded' + '.jpg'
12 | dir_path = os.path.dirname(os.path.realpath(__file__))
13 | subprocess.call([dir_path + '/embed', filepath, message, encoded_filepath])
14 | return encoded_filepath
15 |
16 |
17 | def decode(filepath):
18 | """ Decodes an image based on the stego used in this repo """
19 | dir_path = os.path.dirname(os.path.realpath(__file__))
20 | decoded_text = subprocess.check_output([dir_path + '/decode', filepath])[0:-1]
21 | print 'Decoded_text: '
22 | print decoded_text
23 | return decoded_text
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | *.egg-info/
23 | .installed.cfg
24 | *.egg
25 |
26 | # PyInstaller
27 | # Usually these files are written by a python script from a template
28 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
29 | *.manifest
30 | *.spec
31 |
32 | # Installer logs
33 | pip-log.txt
34 | pip-delete-this-directory.txt
35 |
36 | # Unit test / coverage reports
37 | htmlcov/
38 | .tox/
39 | .coverage
40 | .coverage.*
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 | *,cover
45 |
46 | # Translations
47 | *.mo
48 | *.pot
49 |
50 | # Django stuff:
51 | *.log
52 |
53 | # Sphinx documentation
54 | docs/_build/
55 |
56 | # PyBuilder
57 | target/
58 |
59 | *.sublime-project
60 | *.sublime-workspace
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Endgame, Inc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/img_prep/img_prep.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | from PIL import Image
3 | #requires ImageMagick
4 | import array
5 |
6 |
7 | QTABLEARRAY = {0: array.array('b', [6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, 10, 10, 9, 9, 10, 20, 14,
8 | 15, 12, 16, 23, 20, 24, 24, 23, 20, 22, 22, 26, 29, 37, 31, 26, 27, 35, 28,
9 | 22, 22, 32, 44, 32, 35, 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37,
10 | 40, 41, 40]),
11 | 1: array.array('b', [7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40, 40, 40, 40, 40,
12 | 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
13 | 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
14 | 40, 40, 40, 40, 40, 40])}
15 |
16 |
17 | def prep_img(filepath):
18 | """ Resizes an image to at max 1080x1080 square and re-quantizes with Instagram's quantization table"""
19 | target = filepath[:-4] + '_prepped' + '.jpg'
20 |
21 | with Image.open(filepath) as img:
22 | width, height = img.size
23 |
24 | min_dim = min(width, height)
25 | if min_dim < 1080:
26 | img_dim = str(min_dim) + 'x' + str(min_dim) + '!'
27 | else:
28 | img_dim = '1080x1080!'
29 |
30 | subprocess.call(['convert', '-resize', img_dim, filepath, target])
31 |
32 | with Image.open(target) as img:
33 | img.save(target, qtables=QTABLEARRAY, **img.info)
34 |
35 | return target
36 |
--------------------------------------------------------------------------------
/api_access/insta_access.py:
--------------------------------------------------------------------------------
1 | import InstagramAPI
2 | import requests
3 | import urllib
4 |
5 |
6 | # Hat tip to the following for initial Instagram API work
7 | # https://github.com/LevPasha/Instagram-API-python (WTFPL License)
8 | # https://github.com/mgp25/Instagram-API (MIT License)
9 |
10 |
11 | def upload(filepath, uname, pword):
12 | """
13 | Uploads a given image to the supplied user account with the supplied password
14 | Image must be a jpg and square
15 | """
16 | print 'Uploading ' + filepath + '\n'
17 | insta = InstagramAPI.InstagramAPI(uname, pword)
18 | insta.uploadPhoto(filepath)
19 |
20 |
21 | def download(uname=None, img_url=None, save_location=None):
22 | """
23 | Downloads an input img_url or the latest img from an Instagram account.
24 | """
25 | if img_url:
26 | print 'Downloading input img'
27 | elif uname:
28 | print 'Downloading latest img from ' + uname + '\n'
29 | insta_url = 'https://www.instagram.com/' + uname
30 | img_url = get_latest_img_url(insta_url)
31 | else:
32 | print 'Please input either a uname or img_url'
33 | if not save_location:
34 | save_location = 'download.jpg'
35 | if img_url and save_location:
36 | urllib.urlretrieve(img_url, save_location)
37 | return save_location
38 |
39 |
40 | def get_latest_img_url(address):
41 | """
42 | Grabs the text of the front page for an Instagram user and finds the url for the latest image posted.
43 | """
44 | r = requests.get(address)
45 | p1 = '"display_src": "'
46 | p2 = "?"
47 | loc1 = r.text.find(p1)
48 | loc2 = loc1 + r.text[loc1:].find(p2)
49 | return r.text[loc1 + len(p1):loc2]
50 |
--------------------------------------------------------------------------------
/stego/bitmap.c:
--------------------------------------------------------------------------------
1 | #include "bitmap.h"
2 |
3 | /* CAREFUL WITH pos AND BITMAP SIZE! */
4 |
5 | bool bitmapGet(const byte *bitmap, int pos) {
6 | /* gets the value of the bit at pos */
7 | return get(bitmap[pos/BIT], pos%BIT);
8 | }
9 |
10 | void bitmapSet(byte *bitmap, int pos) {
11 | /* sets bit at pos to 1 */
12 | set(&bitmap[pos/BIT], pos%BIT);
13 | }
14 |
15 | void bitmapClear(byte *bitmap, int pos) {
16 | /* sets bit at pos to 0 */
17 | clear(&bitmap[pos/BIT], pos%BIT);
18 | }
19 |
20 | void bitmapFlip (byte *bitmap, int pos) {
21 | xor( &bitmap[pos/BIT], pos%BIT );
22 | }
23 |
24 |
25 | void bitmapAssign( byte *bitmap, int pos, bool bit) {
26 | /* essentially performs: bitmap[pos]=bit */
27 | assign( &bitmap[pos/BIT], pos%BIT, bit ? 1 : 0 );
28 | }
29 |
30 |
31 | int bitmapSearch(byte *bitmap, bool n, int size, int start) {
32 | /* Finds the first n value in bitmap after start */
33 | /* size is the Bitmap size in bytes */
34 | int i;
35 | /* size is now the Bitmap size in bits */
36 | for(i = start+1, size *= BIT; i < size; i++)
37 | if(bitmapGet(bitmap,i) == n)
38 | return i;
39 | return BITMAP_NOTFOUND;
40 | }
41 |
42 | bool get(byte a, byte pos) {
43 | /* pos is something from 0 to 7*/
44 | return (a >> pos) & 1;
45 | }
46 |
47 | void set(byte *a, byte pos) {
48 | /* pos is something from 0 to 7*/
49 | /* sets bit to 1 */
50 | *a |= 1 << pos;
51 | }
52 |
53 |
54 | void assign(byte *a, byte pos, byte bitval) {
55 | byte assign = bitval << pos;
56 | byte bitnum = 1 << pos;
57 | *a = ( *a & (0xff ^ bitnum)) | assign;
58 | }
59 |
60 |
61 | void xor(byte *a, byte pos) {
62 | *a ^= 1<< pos;
63 | }
64 |
65 | void clear(byte *a, byte pos) {
66 | /* pos is something from 0 to 7*/
67 | /* sets bit to 0 */
68 | *a &= ~(1 << pos);
69 | }
70 |
--------------------------------------------------------------------------------
/instegogram.py:
--------------------------------------------------------------------------------
1 | import os
2 | from time import sleep
3 | from img_prep import img_prep
4 | from stego import stego
5 | from api_access import insta_access
6 |
7 |
8 | def insta_upload(filepath, uname, pword, message=None):
9 | """
10 | Uploads a given image to the supplied user account with the supplied password.
11 | If a message txt file is supplied, the image will be prepped and encoded with that text before upload.
12 | """
13 | if not os.path.isfile(filepath):
14 | print 'Please correct img filepath'
15 |
16 | elif message and not os.path.isfile(message):
17 | print 'Please correct message filepath'
18 |
19 | else:
20 | valid_message = message and message[-3:]
21 | if valid_message:
22 | prepped_filepath = img_prep.prep_img(filepath)
23 | encoded_filepath = stego.encode(prepped_filepath, message)
24 | else:
25 | print 'Message pre-encoded'
26 | encoded_filepath = filepath
27 | insta_access.upload(encoded_filepath, uname, pword)
28 |
29 |
30 | def insta_download_latest(uname):
31 | """ Downloads and decodes the latest image posted to a given Instagram account """
32 | latest_img = insta_access.download(uname)
33 | decoded_text = stego.decode(latest_img)
34 | return decoded_text
35 |
36 |
37 | def insta_roundtrip(filepath, uname, pword, message=None):
38 | """
39 | Encoded, uploads, and downloads an image.
40 | Finally decodes the image and checks again the original message.
41 | """
42 | insta_upload(filepath=filepath, uname=uname, pword=pword, message=message)
43 | sleep(5)
44 | decoded_text = insta_download_latest(uname)
45 | if message and message[-3:] and os.path.isfile(message):
46 | with open(message, 'r') as f:
47 | message_txt = ''.join(f.readlines())
48 | if decoded_text == message_txt:
49 | print 'Successful round trip'
50 | else:
51 | print 'Round trip error'
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Instegogram
2 |
3 | ### Overview
4 | Using Instagram and steganography as a way to transmit images encoded with text by hiding it in prevelant social media traffic.
5 |
6 |
7 | ### Basic Use
8 | Upload image with encoded text:
9 | ```python
10 | import instegogram
11 | instegogram.insta_upload(filepath='examples/piercing.jpg', uname='username', pword='password', message='examples/message.txt')
12 | ```
13 | Download latest image and decode text:
14 | ```python
15 | instegogram.insta_download_latest(uname='username')
16 | ```
17 |
18 | Do both to check that it works:
19 | ```python
20 | instegogram.insta_roundtrip(filepath='examples/piercing.jpg', uname='username', pword='password', message='examples/message.txt')
21 | ```
22 |
23 |
24 | ### Image Prep
25 | Image prep converts an image to square (not perserving aspect ratio) with a maximum size of 1080x1080, which is Instagram's max upload size. It also re-quantizes the image with Instagram's quantization table. This requires [ImageMagic](http://www.imagemagick.org/script/index.php).
26 | ```python
27 | from img_prep import img_prep
28 | img_prep.prep_img(filepath='examples/piercing.jpg')
29 | ```
30 |
31 |
32 | ### Stego
33 | The example stego technique in C is implemented and called via Python. If you have another method for encoding/decoding you can replace the `subprocess` calls in stego.py with your own.
34 |
35 | To encode a text file into an image:
36 | ```python
37 | from stego import stego
38 | stego.encode(filepath='examples/piercing.jpg', message='examples/message.txt')
39 | ```
40 | To decode text from an image:
41 | ```python
42 | stego.deocde(filepath='download.jpg')
43 | ```
44 |
45 | Note: Different images will have different capacities for encoding text. Try to select an image with high average texture in the luminance channel for the 8px by 8px blocks of the image. Or just trial and error until you find a good one (think lots of contrast and not computer generated)
46 |
47 |
48 | ### Instagram API
49 | Instagram is accessed via their API and scraping their website.
50 |
51 | To upload an image:
52 | ```python
53 | from api_access import insta_access
54 | insta_access.upload(filepath='examples/piercing.jpg', uname='username', pword='password')
55 | ```
56 |
57 | To download the latest image:
58 | ```python
59 | insta_access.download(uname='username')
60 | ```
61 | or a specific image (found via page source or other means):
62 | ```python
63 | insta_access.download(img_url='https://scontent-sjc2-1.cdninstagram.com/t51.2885-15/s1080x1080/e15/fr/12960199_1704537009805057_827148056_n.jpg')
64 | ```
65 |
66 |
--------------------------------------------------------------------------------
/stego/jinclude.h:
--------------------------------------------------------------------------------
1 | /*
2 | * jinclude.h
3 | *
4 | * Copyright (C) 1991-1994, Thomas G. Lane.
5 | * This file is part of the Independent JPEG Group's software.
6 | * For conditions of distribution and use, see the accompanying README file.
7 | *
8 | * This file exists to provide a single place to fix any problems with
9 | * including the wrong system include files. (Common problems are taken
10 | * care of by the standard jconfig symbols, but on really weird systems
11 | * you may have to edit this file.)
12 | *
13 | * NOTE: this file is NOT intended to be included by applications using the
14 | * JPEG library. Most applications need only include jpeglib.h.
15 | */
16 |
17 |
18 | /* Include auto-config file to find out which system include files we need. */
19 |
20 | #include "jconfig.h" /* auto configuration options */
21 | #define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */
22 |
23 | /*
24 | * We need the NULL macro and size_t typedef.
25 | * On an ANSI-conforming system it is sufficient to include .
26 | * Otherwise, we get them from or ; we may have to
27 | * pull in as well.
28 | * Note that the core JPEG library does not require ;
29 | * only the default error handler and data source/destination modules do.
30 | * But we must pull it in because of the references to FILE in jpeglib.h.
31 | * You can remove those references if you want to compile without .
32 | */
33 |
34 | #ifdef HAVE_STDDEF_H
35 | #include
36 | #endif
37 |
38 | #ifdef HAVE_STDLIB_H
39 | #include
40 | #endif
41 |
42 | #ifdef NEED_SYS_TYPES_H
43 | #include
44 | #endif
45 |
46 | #include
47 |
48 | /*
49 | * We need memory copying and zeroing functions, plus strncpy().
50 | * ANSI and System V implementations declare these in .
51 | * BSD doesn't have the mem() functions, but it does have bcopy()/bzero().
52 | * Some systems may declare memset and memcpy in .
53 | *
54 | * NOTE: we assume the size parameters to these functions are of type size_t.
55 | * Change the casts in these macros if not!
56 | */
57 |
58 | #ifdef NEED_BSD_STRINGS
59 |
60 | #include
61 | #define MEMZERO(target,size) bzero((void *)(target), (size_t)(size))
62 | #define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size))
63 |
64 | #else /* not BSD, assume ANSI/SysV string lib */
65 |
66 | #include
67 | #define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size))
68 | #define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size))
69 |
70 | #endif
71 |
72 | /*
73 | * In ANSI C, and indeed any rational implementation, size_t is also the
74 | * type returned by sizeof(). However, it seems there are some irrational
75 | * implementations out there, in which sizeof() returns an int even though
76 | * size_t is defined as long or unsigned long. To ensure consistent results
77 | * we always use this SIZEOF() macro in place of using sizeof() directly.
78 | */
79 |
80 | #define SIZEOF(object) ((size_t) sizeof(object))
81 |
82 | /*
83 | * The modules that use fread() and fwrite() always invoke them through
84 | * these macros. On some systems you may need to twiddle the argument casts.
85 | * CAUTION: argument order is different from underlying functions!
86 | */
87 |
88 | #define JFREAD(file,buf,sizeofbuf) \
89 | ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
90 | #define JFWRITE(file,buf,sizeofbuf) \
91 | ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file)))
92 |
--------------------------------------------------------------------------------
/stego/decode.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "cdjpeg.h" /* Common decls for jpeg files */
5 |
6 | // adapted from code from: http://www.cs.odu.edu/~cs772/demos/steganography/sshettyLinux/
7 | //USAGE: ./decode
8 |
9 | #define MAX_MSG_BITS 132000
10 | #define MAX_MSG_BYTES 33000
11 |
12 | void JPEG_extractbits( const char * input_filename, char **message, size_t *message_len) { // returns a null-terminating string that must be freed
13 | struct jpeg_decompress_struct srcinfo;
14 | struct jpeg_error_mgr jsrcerr, jdsterr;
15 | jvirt_barray_ptr *coef_arrays;
16 | JDIMENSION i, compnum, rownum, blocknum;
17 | size_t block_row_size;
18 | JBLOCKARRAY row_ptrs[MAX_COMPONENTS];
19 |
20 | FILE * input_file;
21 |
22 | char mask[MAX_MSG_BITS];
23 | char * decode_int = calloc( MAX_MSG_BYTES, sizeof(char));
24 |
25 | int text = 0, length, length_set = 0, data_set = 0 , count_bits = 0, test_mask = 0, l = 0;
26 | int get_a = 0, get_b = 0, val_a = 0, val_b = 0;
27 |
28 | srcinfo.err = jpeg_std_error(&jsrcerr);
29 | jpeg_create_decompress(&srcinfo);
30 | jsrcerr.trace_level = jdsterr.trace_level;
31 |
32 | input_file = fopen(input_filename, "rb");
33 |
34 | jpeg_stdio_src(&srcinfo, input_file);
35 | (void) jpeg_read_header(&srcinfo, TRUE);
36 |
37 | coef_arrays = jpeg_read_coefficients(&srcinfo);
38 | for (compnum = 0; compnum < srcinfo.num_components; compnum++)
39 | {
40 | block_row_size = (size_t) SIZEOF(JCOEF) * DCTSIZE2
41 | * srcinfo.comp_info[compnum].width_in_blocks;
42 | for (rownum = 0; rownum < srcinfo.comp_info[compnum].height_in_blocks; rownum++)
43 | {
44 | int k , l, j;
45 | row_ptrs[compnum] = ((&srcinfo)->mem->access_virt_barray)
46 | ((j_common_ptr) &srcinfo, coef_arrays[compnum],
47 | rownum, (JDIMENSION) 1, FALSE);
48 | for (blocknum = 0; blocknum < srcinfo.comp_info[compnum].width_in_blocks;
49 | blocknum++)
50 | {
51 | k = -1; l = 0;
52 | for (i = 0; i < DCTSIZE2; i++)
53 | {
54 | if (!length_set)
55 | {
56 | if ( (i > 0 ) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 15))
57 | {
58 | int test_amask = 0x08, k = 0;
59 | int test = abs (row_ptrs[compnum][0][blocknum][i]);
60 | if (get_a == 1 && get_b == 1 )
61 | {
62 | val_b *= 8;
63 | length_set = 1;
64 | }
65 | if (!get_a )
66 | {
67 | get_a = 1;
68 |
69 | for ( k = 0 ; k < 4; k++)
70 | {
71 | if (test & test_amask)
72 | val_a += test_amask;
73 | test_amask = test_amask >> 1;
74 | }
75 | }
76 | else
77 | {
78 |
79 | if (test & 0x01) val_b += pow(2, val_a);
80 | val_a -= 1;
81 | if (val_a == 0 ) get_b = 1;
82 | }
83 |
84 | }
85 | }
86 | else
87 | {
88 | if (!data_set)
89 | {
90 | if ( (i > 0 ) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 1))
91 | {
92 | int test_mask = 2, k = 0;
93 | int test = abs (row_ptrs[compnum][0][blocknum][i]);
94 | if ( test >= 4)
95 | {
96 | while (test_mask)
97 | {
98 | if (test & test_mask)
99 | mask[count_bits] = mask[count_bits] | test_mask;
100 | test_mask = test_mask >> 1;
101 | }
102 | count_bits++;
103 | if ( count_bits == (val_b / 2) ) data_set = 1;
104 | }
105 | }
106 | }
107 | }
108 |
109 | }
110 | }
111 |
112 | }
113 | }
114 |
115 | i = 0;
116 | l = 0;
117 | while ( i < count_bits)
118 | {
119 | int k;
120 | int decode_mask = 0x80;
121 | if (!test_mask) test_mask = 0x02;
122 | while (decode_mask)
123 | {
124 | if ( mask[i] & test_mask)
125 | decode_int[l] += decode_mask;
126 | test_mask >>= 1;
127 | if (!test_mask) { i++; test_mask = 0x02; }
128 |
129 | decode_mask >>= 1;
130 | }
131 | l++;
132 | }
133 | (void) jpeg_finish_decompress(&srcinfo);
134 | jpeg_destroy_decompress(&srcinfo);
135 | fclose (input_file);
136 | decode_int[l] = 0; // null terminating
137 |
138 | *message_len = l;
139 | *message = decode_int;
140 | };
141 |
142 |
143 | int main (int argc, char **argv)
144 | {
145 | if ( argc == 2)
146 | {
147 | char * message;
148 | size_t message_len;
149 | JPEG_extractbits( argv[1], &message, &message_len);
150 | printf("%s\n", message);
151 | free(message);
152 | }
153 |
154 | else
155 |
156 | {
157 | printf ("USAGE : \n");
158 | printf ("./decode \n");
159 | }
160 |
161 | return 0; /* suppress no-return-value warnings */
162 | }
163 |
164 |
--------------------------------------------------------------------------------
/stego/cderror.h:
--------------------------------------------------------------------------------
1 | /*
2 | * cderror.h
3 | *
4 | * Copyright (C) 1994-1997, Thomas G. Lane.
5 | * This file is part of the Independent JPEG Group's software.
6 | * For conditions of distribution and use, see the accompanying README file.
7 | *
8 | * This file defines the error and message codes for the cjpeg/djpeg
9 | * applications. These strings are not needed as part of the JPEG library
10 | * proper.
11 | * Edit this file to add new codes, or to translate the message strings to
12 | * some other language.
13 | */
14 |
15 | /*
16 | * To define the enum list of message codes, include this file without
17 | * defining macro JMESSAGE. To create a message string table, include it
18 | * again with a suitable JMESSAGE definition (see jerror.c for an example).
19 | */
20 | #ifndef JMESSAGE
21 | #ifndef CDERROR_H
22 | #define CDERROR_H
23 | /* First time through, define the enum list */
24 | #define JMAKE_ENUM_LIST
25 | #else
26 | /* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */
27 | #define JMESSAGE(code,string)
28 | #endif /* CDERROR_H */
29 | #endif /* JMESSAGE */
30 |
31 | #ifdef JMAKE_ENUM_LIST
32 |
33 | typedef enum {
34 |
35 | #define JMESSAGE(code,string) code ,
36 |
37 | #endif /* JMAKE_ENUM_LIST */
38 |
39 | JMESSAGE(JMSG_FIRSTADDONCODE=1000, NULL) /* Must be first entry! */
40 |
41 | #ifdef BMP_SUPPORTED
42 | JMESSAGE(JERR_BMP_BADCMAP, "Unsupported BMP colormap format")
43 | JMESSAGE(JERR_BMP_BADDEPTH, "Only 8- and 24-bit BMP files are supported")
44 | JMESSAGE(JERR_BMP_BADHEADER, "Invalid BMP file: bad header length")
45 | JMESSAGE(JERR_BMP_BADPLANES, "Invalid BMP file: biPlanes not equal to 1")
46 | JMESSAGE(JERR_BMP_COLORSPACE, "BMP output must be grayscale or RGB")
47 | JMESSAGE(JERR_BMP_COMPRESSED, "Sorry, compressed BMPs not yet supported")
48 | JMESSAGE(JERR_BMP_NOT, "Not a BMP file - does not start with BM")
49 | JMESSAGE(JTRC_BMP, "%ux%u 24-bit BMP image")
50 | JMESSAGE(JTRC_BMP_MAPPED, "%ux%u 8-bit colormapped BMP image")
51 | JMESSAGE(JTRC_BMP_OS2, "%ux%u 24-bit OS2 BMP image")
52 | JMESSAGE(JTRC_BMP_OS2_MAPPED, "%ux%u 8-bit colormapped OS2 BMP image")
53 | #endif /* BMP_SUPPORTED */
54 |
55 | #ifdef GIF_SUPPORTED
56 | JMESSAGE(JERR_GIF_BUG, "GIF output got confused")
57 | JMESSAGE(JERR_GIF_CODESIZE, "Bogus GIF codesize %d")
58 | JMESSAGE(JERR_GIF_COLORSPACE, "GIF output must be grayscale or RGB")
59 | JMESSAGE(JERR_GIF_IMAGENOTFOUND, "Too few images in GIF file")
60 | JMESSAGE(JERR_GIF_NOT, "Not a GIF file")
61 | JMESSAGE(JTRC_GIF, "%ux%ux%d GIF image")
62 | JMESSAGE(JTRC_GIF_BADVERSION,
63 | "Warning: unexpected GIF version number '%c%c%c'")
64 | JMESSAGE(JTRC_GIF_EXTENSION, "Ignoring GIF extension block of type 0x%02x")
65 | JMESSAGE(JTRC_GIF_NONSQUARE, "Caution: nonsquare pixels in input")
66 | JMESSAGE(JWRN_GIF_BADDATA, "Corrupt data in GIF file")
67 | JMESSAGE(JWRN_GIF_CHAR, "Bogus char 0x%02x in GIF file, ignoring")
68 | JMESSAGE(JWRN_GIF_ENDCODE, "Premature end of GIF image")
69 | JMESSAGE(JWRN_GIF_NOMOREDATA, "Ran out of GIF bits")
70 | #endif /* GIF_SUPPORTED */
71 |
72 | #ifdef PPM_SUPPORTED
73 | JMESSAGE(JERR_PPM_COLORSPACE, "PPM output must be grayscale or RGB")
74 | JMESSAGE(JERR_PPM_NONNUMERIC, "Nonnumeric data in PPM file")
75 | JMESSAGE(JERR_PPM_NOT, "Not a PPM/PGM file")
76 | JMESSAGE(JTRC_PGM, "%ux%u PGM image")
77 | JMESSAGE(JTRC_PGM_TEXT, "%ux%u text PGM image")
78 | JMESSAGE(JTRC_PPM, "%ux%u PPM image")
79 | JMESSAGE(JTRC_PPM_TEXT, "%ux%u text PPM image")
80 | #endif /* PPM_SUPPORTED */
81 |
82 | #ifdef RLE_SUPPORTED
83 | JMESSAGE(JERR_RLE_BADERROR, "Bogus error code from RLE library")
84 | JMESSAGE(JERR_RLE_COLORSPACE, "RLE output must be grayscale or RGB")
85 | JMESSAGE(JERR_RLE_DIMENSIONS, "Image dimensions (%ux%u) too large for RLE")
86 | JMESSAGE(JERR_RLE_EMPTY, "Empty RLE file")
87 | JMESSAGE(JERR_RLE_EOF, "Premature EOF in RLE header")
88 | JMESSAGE(JERR_RLE_MEM, "Insufficient memory for RLE header")
89 | JMESSAGE(JERR_RLE_NOT, "Not an RLE file")
90 | JMESSAGE(JERR_RLE_TOOMANYCHANNELS, "Cannot handle %d output channels for RLE")
91 | JMESSAGE(JERR_RLE_UNSUPPORTED, "Cannot handle this RLE setup")
92 | JMESSAGE(JTRC_RLE, "%ux%u full-color RLE file")
93 | JMESSAGE(JTRC_RLE_FULLMAP, "%ux%u full-color RLE file with map of length %d")
94 | JMESSAGE(JTRC_RLE_GRAY, "%ux%u grayscale RLE file")
95 | JMESSAGE(JTRC_RLE_MAPGRAY, "%ux%u grayscale RLE file with map of length %d")
96 | JMESSAGE(JTRC_RLE_MAPPED, "%ux%u colormapped RLE file with map of length %d")
97 | #endif /* RLE_SUPPORTED */
98 |
99 | #ifdef TARGA_SUPPORTED
100 | JMESSAGE(JERR_TGA_BADCMAP, "Unsupported Targa colormap format")
101 | JMESSAGE(JERR_TGA_BADPARMS, "Invalid or unsupported Targa file")
102 | JMESSAGE(JERR_TGA_COLORSPACE, "Targa output must be grayscale or RGB")
103 | JMESSAGE(JTRC_TGA, "%ux%u RGB Targa image")
104 | JMESSAGE(JTRC_TGA_GRAY, "%ux%u grayscale Targa image")
105 | JMESSAGE(JTRC_TGA_MAPPED, "%ux%u colormapped Targa image")
106 | #else
107 | JMESSAGE(JERR_TGA_NOTCOMP, "Targa support was not compiled")
108 | #endif /* TARGA_SUPPORTED */
109 |
110 | JMESSAGE(JERR_BAD_CMAP_FILE,
111 | "Color map file is invalid or of unsupported format")
112 | JMESSAGE(JERR_TOO_MANY_COLORS,
113 | "Output file format cannot handle %d colormap entries")
114 | JMESSAGE(JERR_UNGETC_FAILED, "ungetc failed")
115 | #ifdef TARGA_SUPPORTED
116 | JMESSAGE(JERR_UNKNOWN_FORMAT,
117 | "Unrecognized input file format --- perhaps you need -targa")
118 | #else
119 | JMESSAGE(JERR_UNKNOWN_FORMAT, "Unrecognized input file format")
120 | #endif
121 | JMESSAGE(JERR_UNSUPPORTED_FORMAT, "Unsupported output file format")
122 |
123 | #ifdef JMAKE_ENUM_LIST
124 |
125 | JMSG_LASTADDONCODE
126 | } ADDON_MESSAGE_CODE;
127 |
128 | #undef JMAKE_ENUM_LIST
129 | #endif /* JMAKE_ENUM_LIST */
130 |
131 | /* Zap JMESSAGE macro so that future re-inclusions do nothing by default */
132 | #undef JMESSAGE
133 |
--------------------------------------------------------------------------------
/stego/cdjpeg.h:
--------------------------------------------------------------------------------
1 | /*
2 | * cdjpeg.h
3 | *
4 | * Copyright (C) 1994-1997, Thomas G. Lane.
5 | * This file is part of the Independent JPEG Group's software.
6 | * For conditions of distribution and use, see the accompanying README file.
7 | *
8 | * This file contains common declarations for the sample applications
9 | * cjpeg and djpeg. It is NOT used by the core JPEG library.
10 | */
11 |
12 | #define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */
13 | #define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */
14 | #include "jinclude.h"
15 | #include "jpeglib.h"
16 | #include "jerror.h" /* get library error codes too */
17 | #include "cderror.h" /* get application-specific error codes */
18 |
19 |
20 | /*
21 | * Object interface for cjpeg's source file decoding modules
22 | */
23 |
24 | typedef struct cjpeg_source_struct * cjpeg_source_ptr;
25 |
26 | struct cjpeg_source_struct {
27 | JMETHOD(void, start_input, (j_compress_ptr cinfo,
28 | cjpeg_source_ptr sinfo));
29 | JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo,
30 | cjpeg_source_ptr sinfo));
31 | JMETHOD(void, finish_input, (j_compress_ptr cinfo,
32 | cjpeg_source_ptr sinfo));
33 |
34 | FILE *input_file;
35 |
36 | JSAMPARRAY buffer;
37 | JDIMENSION buffer_height;
38 | };
39 |
40 |
41 | /*
42 | * Object interface for djpeg's output file encoding modules
43 | */
44 |
45 | typedef struct djpeg_dest_struct * djpeg_dest_ptr;
46 |
47 | struct djpeg_dest_struct {
48 | /* start_output is called after jpeg_start_decompress finishes.
49 | * The color map will be ready at this time, if one is needed.
50 | */
51 | JMETHOD(void, start_output, (j_decompress_ptr cinfo,
52 | djpeg_dest_ptr dinfo));
53 | /* Emit the specified number of pixel rows from the buffer. */
54 | JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo,
55 | djpeg_dest_ptr dinfo,
56 | JDIMENSION rows_supplied));
57 | /* Finish up at the end of the image. */
58 | JMETHOD(void, finish_output, (j_decompress_ptr cinfo,
59 | djpeg_dest_ptr dinfo));
60 |
61 | /* Target file spec; filled in by djpeg.c after object is created. */
62 | FILE * output_file;
63 |
64 | /* Output pixel-row buffer. Created by module init or start_output.
65 | * Width is cinfo->output_width * cinfo->output_components;
66 | * height is buffer_height.
67 | */
68 | JSAMPARRAY buffer;
69 | JDIMENSION buffer_height;
70 | };
71 |
72 |
73 | /*
74 | * cjpeg/djpeg may need to perform extra passes to convert to or from
75 | * the source/destination file format. The JPEG library does not know
76 | * about these passes, but we'd like them to be counted by the progress
77 | * monitor. We use an expanded progress monitor object to hold the
78 | * additional pass count.
79 | */
80 |
81 | struct cdjpeg_progress_mgr {
82 | struct jpeg_progress_mgr pub; /* fields known to JPEG library */
83 | int completed_extra_passes; /* extra passes completed */
84 | int total_extra_passes; /* total extra */
85 | /* last printed percentage stored here to avoid multiple printouts */
86 | int percent_done;
87 | };
88 |
89 | typedef struct cdjpeg_progress_mgr * cd_progress_ptr;
90 |
91 |
92 | /* Short forms of external names for systems with brain-damaged linkers. */
93 |
94 | #ifdef NEED_SHORT_EXTERNAL_NAMES
95 | #define jinit_read_bmp jIRdBMP
96 | #define jinit_write_bmp jIWrBMP
97 | #define jinit_read_gif jIRdGIF
98 | #define jinit_write_gif jIWrGIF
99 | #define jinit_read_ppm jIRdPPM
100 | #define jinit_write_ppm jIWrPPM
101 | #define jinit_read_rle jIRdRLE
102 | #define jinit_write_rle jIWrRLE
103 | #define jinit_read_targa jIRdTarga
104 | #define jinit_write_targa jIWrTarga
105 | #define read_quant_tables RdQTables
106 | #define read_scan_script RdScnScript
107 | #define set_quant_slots SetQSlots
108 | #define set_sample_factors SetSFacts
109 | #define read_color_map RdCMap
110 | #define enable_signal_catcher EnSigCatcher
111 | #define start_progress_monitor StProgMon
112 | #define end_progress_monitor EnProgMon
113 | #define read_stdin RdStdin
114 | #define write_stdout WrStdout
115 | #endif /* NEED_SHORT_EXTERNAL_NAMES */
116 |
117 | /* Module selection routines for I/O modules. */
118 |
119 | EXTERN(cjpeg_source_ptr) jinit_read_bmp JPP((j_compress_ptr cinfo));
120 | EXTERN(djpeg_dest_ptr) jinit_write_bmp JPP((j_decompress_ptr cinfo,
121 | boolean is_os2));
122 | EXTERN(cjpeg_source_ptr) jinit_read_gif JPP((j_compress_ptr cinfo));
123 | EXTERN(djpeg_dest_ptr) jinit_write_gif JPP((j_decompress_ptr cinfo));
124 | EXTERN(cjpeg_source_ptr) jinit_read_ppm JPP((j_compress_ptr cinfo));
125 | EXTERN(djpeg_dest_ptr) jinit_write_ppm JPP((j_decompress_ptr cinfo));
126 | EXTERN(cjpeg_source_ptr) jinit_read_rle JPP((j_compress_ptr cinfo));
127 | EXTERN(djpeg_dest_ptr) jinit_write_rle JPP((j_decompress_ptr cinfo));
128 | EXTERN(cjpeg_source_ptr) jinit_read_targa JPP((j_compress_ptr cinfo));
129 | EXTERN(djpeg_dest_ptr) jinit_write_targa JPP((j_decompress_ptr cinfo));
130 |
131 | /* cjpeg support routines (in rdswitch.c) */
132 |
133 | EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename,
134 | int scale_factor, boolean force_baseline));
135 | EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename));
136 | EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg));
137 | EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg));
138 |
139 | /* djpeg support routines (in rdcolmap.c) */
140 |
141 | EXTERN(void) read_color_map JPP((j_decompress_ptr cinfo, FILE * infile));
142 |
143 | /* common support routines (in cdjpeg.c) */
144 |
145 | EXTERN(void) enable_signal_catcher JPP((j_common_ptr cinfo));
146 | EXTERN(void) start_progress_monitor JPP((j_common_ptr cinfo,
147 | cd_progress_ptr progress));
148 | EXTERN(void) end_progress_monitor JPP((j_common_ptr cinfo));
149 | EXTERN(boolean) keymatch JPP((char * arg, const char * keyword, int minchars));
150 | EXTERN(FILE *) read_stdin JPP((void));
151 | EXTERN(FILE *) write_stdout JPP((void));
152 |
153 | /* miscellaneous useful macros */
154 |
155 | #ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */
156 | #define READ_BINARY "r"
157 | #define WRITE_BINARY "w"
158 | #else
159 | #ifdef VMS /* VMS is very nonstandard */
160 | #define READ_BINARY "rb", "ctx=stm"
161 | #define WRITE_BINARY "wb", "ctx=stm"
162 | #else /* standard ANSI-compliant case */
163 | #define READ_BINARY "rb"
164 | #define WRITE_BINARY "wb"
165 | #endif
166 | #endif
167 |
168 | #ifndef EXIT_FAILURE /* define exit() codes if not provided */
169 | #define EXIT_FAILURE 1
170 | #endif
171 | #ifndef EXIT_SUCCESS
172 | #ifdef VMS
173 | #define EXIT_SUCCESS 1 /* VMS is very nonstandard */
174 | #else
175 | #define EXIT_SUCCESS 0
176 | #endif
177 | #endif
178 | #ifndef EXIT_WARNING
179 | #ifdef VMS
180 | #define EXIT_WARNING 1 /* VMS is very nonstandard */
181 | #else
182 | #define EXIT_WARNING 2
183 | #endif
184 | #endif
185 |
--------------------------------------------------------------------------------
/stego/embed.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "cdjpeg.h" /* Common decls for Reading jpeg files */
5 |
6 | // adapted from code from: http://www.cs.odu.edu/~cs772/demos/steganography/sshettyLinux/
7 | // USAGE: ./encode
8 |
9 |
10 | void convert(const char str[], const size_t str_len, char * mask) {
11 | char *result = malloc(str_len*8); // 1 bit for every byte
12 | int i, j, hex_res, k = 0;
13 |
14 | for ( i = 0 ; i < strlen(str) ; i++ )
15 | {
16 | int test_mask = 0x80;
17 | int test = str[i];
18 | for ( j = 0 ; j < 8 ; j++ )
19 | {
20 | int res = test & test_mask;
21 | if (res )
22 | result[k++] = '1';
23 | else result[k++] = '0';
24 | test_mask = test_mask >> 1;
25 | }
26 | }
27 | int result_len = k;
28 | for ( i = 0, j=0 ; i < result_len ; i += 2, j+=1 )
29 | {
30 | mask[j] = 0;
31 | if ( result[i] == '1') mask[j] |= 0x02;
32 | if ( result[i + 1] == '1') mask[j] |= 0x01;
33 | }
34 | free(result);
35 | }
36 |
37 |
38 | void JPEG_copy_encodebits( const char * input_filename, const char * output_filename, const char * str, const size_t str_len) {
39 | struct jpeg_decompress_struct srcinfo;
40 | struct jpeg_compress_struct dstinfo;
41 | struct jpeg_error_mgr jsrcerr, jdsterr;
42 | jvirt_barray_ptr *coef_arrays;
43 | JDIMENSION i, compnum, rownum, blocknum;
44 | size_t block_row_size;
45 | JBLOCKARRAY coef_buffers[MAX_COMPONENTS], zig_zag[MAX_COMPONENTS];
46 | JBLOCKARRAY row_ptrs[MAX_COMPONENTS];
47 |
48 | FILE * input_file = fopen(input_filename , "rb");
49 | FILE * output_file = fopen(output_filename , "wb");
50 |
51 | int filesize, text = 0, length_set = 0, data_set = 0, count_bits = 0;
52 | char *bin_str, *res_str, mess_encr[255], encode_str[255];
53 |
54 | int len_a = 3, val_a = 0, len_b = 0, val_b = 0, test_amask = 0x04, test_bmask;
55 | int len_mask = 0, index, k, l, fp_char = 0, c, temp, elem;
56 |
57 | for ( elem = 0 ; elem <= 15 ; elem++)
58 | {
59 | int temp = pow( 2 , elem);
60 | if (temp > str_len )
61 | {
62 | len_b = elem - 1;
63 | break;
64 | }
65 | }
66 | test_bmask = pow(2, len_b);
67 |
68 | //bin_str = convert(str);
69 | size_t mask_len = str_len*8/2;
70 | char * mask = calloc( mask_len, sizeof(char)); // 2 bits per char
71 | convert(str, str_len, mask);
72 |
73 | srcinfo.err = jpeg_std_error(&jsrcerr);
74 | jpeg_create_decompress(&srcinfo);
75 | dstinfo.err = jpeg_std_error(&jdsterr);
76 | jpeg_create_compress(&dstinfo);
77 | jsrcerr.trace_level = jdsterr.trace_level;
78 | srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
79 |
80 | jpeg_stdio_src(&srcinfo, input_file);
81 |
82 | (void) jpeg_read_header(&srcinfo, TRUE);
83 | for (compnum = 0; compnum < srcinfo.num_components; compnum++)
84 | coef_buffers[compnum] = ((&dstinfo)->mem->alloc_barray)
85 | ((j_common_ptr) &dstinfo, JPOOL_IMAGE,
86 | srcinfo.comp_info[compnum].width_in_blocks,
87 | srcinfo.comp_info[compnum].height_in_blocks);
88 | coef_arrays = jpeg_read_coefficients(&srcinfo);
89 | jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
90 |
91 |
92 | for (compnum = 0; compnum < srcinfo.num_components; compnum++)
93 | {
94 | block_row_size = (size_t) SIZEOF(JCOEF) * DCTSIZE2 * srcinfo.comp_info[compnum].width_in_blocks;
95 |
96 | for (rownum = 0; rownum < srcinfo.comp_info[compnum].height_in_blocks; rownum++)
97 | {
98 | int k , l, tag = 1, j;
99 | row_ptrs[compnum] = ((&dstinfo)->mem->access_virt_barray)
100 | ((j_common_ptr) &dstinfo, coef_arrays[compnum],
101 | rownum, (JDIMENSION) 1, FALSE);
102 | for (blocknum = 0; blocknum < srcinfo.comp_info[compnum].width_in_blocks;
103 | blocknum++)
104 | {
105 | int x, y, count = 0;
106 | k = -1; l = 0;
107 | for (i = 0; i < DCTSIZE2; i++)
108 | {
109 | if (!length_set)
110 | {
111 |
112 | if ( (i > 0) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 15))
113 | {
114 |
115 | int test;
116 | if ( len_a == 0 && len_b == 0 ) length_set = 1;
117 | if (len_a != 0)
118 | {
119 | int test_amask = 0x08;
120 | len_a = 0;
121 | test = abs(row_ptrs[compnum][0][blocknum][i]);
122 | test >>= 4;
123 | for ( k = 0 ; k < 4 ; k++ )
124 | {
125 | test <<= 1;
126 | if ( test_amask & len_b)
127 | test |= 1;
128 | test_amask >>= 1;
129 | }
130 | }
131 | else
132 | {
133 | len_b -= 1;
134 | test = abs(row_ptrs[compnum][0][blocknum][i]);
135 | test >>= 1;
136 | test <<= 1;
137 | if ( str_len & test_bmask) test |= 1;
138 | test_bmask >>= 1;
139 | }
140 |
141 | if ( row_ptrs[compnum][0][blocknum][i] < 0 )
142 | coef_buffers[compnum][rownum][blocknum][i] = -test;
143 |
144 | else
145 | coef_buffers[compnum][rownum][blocknum][i] = test;
146 |
147 | }
148 | else
149 | coef_buffers[compnum][rownum][blocknum][i] =
150 | row_ptrs[compnum][0][blocknum][i] ;
151 | }
152 | else
153 | {
154 | if (!data_set)
155 | {
156 | if ( ( i > 0 ) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 1))
157 | {
158 |
159 | int k = 0;
160 | int test = abs(row_ptrs[compnum][0][blocknum][i]);
161 | if ( test >= 4 )
162 | {
163 | int test_mask = 0x02;
164 | test >>= 2;
165 | for ( k = 0 ; k < 2 ; k++ )
166 | {
167 | test <<= 1;
168 | if ( mask[count_bits] & test_mask)
169 | test |= 0x01;
170 | test_mask >>= 1;
171 | }
172 | if ( test != 0 && test != 1)
173 | {
174 | if ( row_ptrs[compnum][0][blocknum][i] < 0 )
175 | coef_buffers[compnum][rownum][blocknum][i] = -test;
176 | else
177 | coef_buffers[compnum][rownum][blocknum][i] = test;
178 | count_bits++;
179 | }
180 | }
181 | if ( count_bits == mask_len ) data_set = 1;
182 |
183 | }
184 | else coef_buffers[compnum][rownum][blocknum][i] = row_ptrs[compnum][0][blocknum][i] ;
185 | }
186 | else coef_buffers[compnum][rownum][blocknum][i] = row_ptrs[compnum][0][blocknum][i] ;
187 | }
188 |
189 | }
190 | }
191 | }
192 | }
193 | for (compnum = 0; compnum < srcinfo.num_components; compnum++)
194 | {
195 | block_row_size = (size_t) SIZEOF(JCOEF) * DCTSIZE2
196 | * srcinfo.comp_info[compnum].width_in_blocks;
197 | for (rownum = 0; rownum < srcinfo.comp_info[compnum].height_in_blocks; rownum++)
198 | {
199 | int k , l;
200 | row_ptrs[compnum] = ((&dstinfo)->mem->access_virt_barray)
201 | ((j_common_ptr) &dstinfo, coef_arrays[compnum],
202 | rownum, (JDIMENSION) 1, FALSE);
203 | for (blocknum = 0; blocknum < srcinfo.comp_info[compnum].width_in_blocks;
204 | blocknum++)
205 | for (i = 0; i < DCTSIZE2; i++)
206 | row_ptrs[compnum][0][blocknum][i] = coef_buffers[compnum][rownum][blocknum][i] ;
207 | }
208 | }
209 | jpeg_stdio_dest(&dstinfo, output_file);
210 | jpeg_write_coefficients(&dstinfo, coef_arrays);
211 | jpeg_finish_compress(&dstinfo);
212 | jpeg_destroy_compress(&dstinfo);
213 | (void) jpeg_finish_decompress(&srcinfo);
214 | jpeg_destroy_decompress(&srcinfo);
215 | fclose(output_file);
216 | fclose (input_file);
217 | free(mask);
218 | }
219 |
220 |
221 | int
222 | main (int argc, char **argv)
223 | {
224 | if ( argc == 4 )
225 | {
226 | FILE * fp_message;
227 | char *str;
228 | char *bin_str;
229 | char c, fp_char;
230 |
231 | fp_message = fopen(argv[2] , "r");
232 |
233 | /* Read the message to be encoded into the buffer */
234 | str = (char *) malloc (33000);
235 | while ((c = fgetc(fp_message)) != -1)
236 | str[fp_char++] = c;
237 | str[fp_char] = '\0';
238 | fseek(fp_message, 0, SEEK_SET);
239 |
240 | printf ("Message File Read and Converted to Bits\n");
241 |
242 | printf ("Reading High Frequency Components from the Image \n");
243 | JPEG_copy_encodebits(argv[1], argv[3], str, strlen(str));
244 |
245 | free(str);
246 |
247 | } else {
248 | printf ("Usage : \n");
249 | printf ("./embed : \n");
250 | }
251 |
252 | return 0; /* suppress no-return-value warnings */
253 | }
254 |
255 |
--------------------------------------------------------------------------------
/api_access/InstagramAPI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # Forked from https://github.com/LevPasha/Instagram-API-python (WTFPL License)
4 |
5 | import requests
6 | import random
7 | import json
8 | import hashlib
9 | import hmac
10 | import urllib
11 | import time
12 | from PIL import Image
13 | from time import strftime
14 |
15 |
16 | class InstagramAPI():
17 |
18 | API_URL = 'https://i.instagram.com/api/v1/'
19 | USER_AGENT = 'Instagram 8.4.0 Android (18/4.3; 320dpi; 720x1280; Xiaomi; HM 1SW; armani; qcom; en_US)'
20 | IG_SIG_KEY = '3d3d669cedc38f2ea7d198840e0648db3738224a0f661aa6a2c1e77dfa964a1e'
21 | EXPERIMENTS = 'ig_android_progressive_jpeg,ig_creation_growth_holdout,ig_android_report_and_hide,' + \
22 | 'ig_android_new_browser,ig_android_enable_share_to_whatsapp,' + \
23 | 'ig_android_direct_drawing_in_quick_cam_universe,ig_android_huawei_app_badging,' + \
24 | 'ig_android_universe_video_production,ig_android_asus_app_badging,ig_android_direct_plus_button,' + \
25 | 'ig_android_ads_heatmap_overlay_universe,ig_android_http_stack_experiment_2016,' + \
26 | 'ig_android_infinite_scrolling,ig_fbns_blocked,ig_android_white_out_universe,' + \
27 | 'ig_android_full_people_card_in_user_list,ig_android_post_auto_retry_v7_21,ig_fbns_push,' + \
28 | 'ig_android_feed_pill,ig_android_profile_link_iab,ig_explore_v3_us_holdout,' + \
29 | 'ig_android_histogram_reporter,ig_android_anrwatchdog,ig_android_search_client_matching,' + \
30 | 'ig_android_high_res_upload_2,ig_android_new_browser_pre_kitkat,ig_android_2fac,' + \
31 | 'ig_android_grid_video_icon,ig_android_white_camera_universe,ig_android_disable_chroma_subsampling,' + \
32 | 'ig_android_share_spinner,ig_android_explore_people_feed_icon,ig_explore_v3_android_universe,' + \
33 | 'ig_android_media_favorites,ig_android_nux_holdout,ig_android_search_null_state,' + \
34 | 'ig_android_react_native_notification_setting,ig_android_ads_indicator_change_universe,' + \
35 | 'ig_android_video_loading_behavior,ig_android_black_camera_tab,liger_instagram_android_univ,' + \
36 | 'ig_explore_v3_internal,ig_android_direct_emoji_picker,ig_android_prefetch_explore_delay_time,' + \
37 | 'ig_android_business_insights_qe,ig_android_direct_media_size,ig_android_enable_client_share,' + \
38 | 'ig_android_promoted_posts,ig_android_app_badging_holdout,ig_android_ads_cta_universe,' + \
39 | 'ig_android_mini_inbox_2,ig_android_feed_reshare_button_nux,ig_android_boomerang_feed_attribution,' + \
40 | 'ig_android_fbinvite_qe,ig_fbns_shared,ig_android_direct_full_width_media,' + \
41 | 'ig_android_hscroll_profile_chaining,ig_android_feed_unit_footer,ig_android_media_tighten_space,' + \
42 | 'ig_android_private_follow_request,ig_android_inline_gallery_backoff_hours_universe,' + \
43 | 'ig_android_direct_thread_ui_rewrite,ig_android_rendering_controls,' + \
44 | 'ig_android_ads_full_width_cta_universe,ig_video_max_duration_qe_preuniverse,' + \
45 | 'ig_android_prefetch_explore_expire_time,ig_timestamp_public_test,ig_android_profile,' + \
46 | 'ig_android_dv2_consistent_http_realtime_response,ig_android_enable_share_to_messenger,' + \
47 | 'ig_explore_v3,ig_ranking_following,ig_android_pending_request_search_bar,' + \
48 | 'ig_android_feed_ufi_redesign,ig_android_video_pause_logging_fix,ig_android_default_folder_to_camera,' + \
49 | 'ig_android_video_stitching_7_23,ig_android_profanity_filter,ig_android_business_profile_qe,' + \
50 | 'ig_android_search,ig_android_boomerang_entry,ig_android_inline_gallery_universe,' + \
51 | 'ig_android_ads_overlay_design_universe,ig_android_options_app_invite,' + \
52 | 'ig_android_view_count_decouple_likes_universe,ig_android_periodic_analytics_upload_v2,' + \
53 | 'ig_android_feed_unit_hscroll_auto_advance,ig_peek_profile_photo_universe,' + \
54 | 'ig_android_ads_holdout_universe,ig_android_prefetch_explore,ig_android_direct_bubble_icon,' + \
55 | 'ig_video_use_sve_universe,ig_android_inline_gallery_no_backoff_on_launch_universe,' + \
56 | 'ig_android_image_cache_multi_queue,ig_android_camera_nux,ig_android_immersive_viewer,' + \
57 | 'ig_android_dense_feed_unit_cards,ig_android_sqlite_dev,ig_android_exoplayer,ig_android_add_to_last_post,' + \
58 | 'ig_android_direct_public_threads,ig_android_prefetch_venue_in_composer,ig_android_bigger_share_button,' + \
59 | 'ig_android_dv2_realtime_private_share,ig_android_non_square_first,ig_android_video_interleaved_v2,' + \
60 | 'ig_android_follow_search_bar,ig_android_last_edits,ig_android_video_download_logging,' + \
61 | 'ig_android_ads_loop_count_universe,ig_android_swipeable_filters_blacklist,' + \
62 | 'ig_android_boomerang_layout_white_out_universe,ig_android_ads_carousel_multi_row_universe,' + \
63 | 'ig_android_mentions_invite_v2,ig_android_direct_mention_qe,' + \
64 | 'ig_android_following_follower_social_context'
65 | SIG_KEY_VERSION = '4'
66 | IGDataPath = 'data/'
67 |
68 | # username # Instagram username
69 | # password # Instagram password
70 | # debug # Debug
71 | # uuid # UUID
72 | # device_id # Device ID
73 | # username_id # Username ID
74 | # token # _csrftoken
75 | # isLoggedIn # Session status
76 | # rank_token # Rank token
77 | # IGDataPath # Data storage path
78 |
79 | def __init__(self, username, password, debug=False, IGDataPath=None):
80 | m = hashlib.md5()
81 | m.update(username.encode('utf-8') + password.encode('utf-8'))
82 | self.device_id = self.generateDeviceId(m.hexdigest())
83 | self.setUser(username, password)
84 | self.isLoggedIn = False
85 | self.LastResponse = None
86 | self.setUser(username, password)
87 | self.login()
88 |
89 | def setUser(self, username, password):
90 | self.username = username
91 | self.password = password
92 | self.uuid = self.generateUUID(True)
93 |
94 | def login(self, force=False):
95 | if (not self.isLoggedIn or force):
96 | self.s = requests.Session()
97 | # if you need proxy make something like this:
98 | # self.s.proxies = {"https" : "http://proxyip:proxyport"}
99 | if (self.SendRequest('si/fetch_headers/?challenge_type=signup&guid=' + self.generateUUID(False),
100 | None, True)):
101 | data = {'phone_id': self.generateUUID(True),
102 | '_csrftoken': self.LastResponse.cookies['csrftoken'],
103 | 'username': self.username,
104 | 'guid': self.uuid,
105 | 'device_id': self.device_id,
106 | 'password': self.password,
107 | 'login_attempt_count': '0'}
108 |
109 | attempt = self.SendRequest('accounts/login/', self.generateSignature(json.dumps(data)), True)
110 | if (attempt):
111 | print self.LastJson
112 | self.sessionid = self.s.cookies['sessionid']
113 | self.isLoggedIn = True
114 | self.username_id = self.LastJson["logged_in_user"]["pk"]
115 | self.rank_token = "%s_%s" % (self.username_id, self.uuid)
116 | self.token = self.LastResponse.cookies["csrftoken"]
117 |
118 | self.syncFeatures()
119 | print ("Login success!\n")
120 | return True
121 |
122 | def syncFeatures(self):
123 | data = json.dumps({
124 | '_uuid': self.uuid,
125 | '_uid': self.username_id,
126 | 'id': self.username_id,
127 | '_csrftoken': self.token,
128 | 'experiments': self.EXPERIMENTS
129 | })
130 | return self.SendRequest('qe/sync/', self.generateSignature(data))
131 |
132 | def expose(self):
133 | data = json.dumps({
134 | '_uuid': self.uuid,
135 | '_uid': self.username_id,
136 | 'id': self.username_id,
137 | '_csrftoken': self.token,
138 | 'experiment': 'ig_android_profile_contextual_feed'
139 | })
140 | return self.SendRequest('qe/expose/', self.generateSignature(data))
141 |
142 | def buildBody(self, upload_id, uuid, token, filepath):
143 | boundary = uuid
144 |
145 | bodies = [
146 | {
147 | 'type': 'form-data',
148 | 'name': 'upload_id',
149 | 'data': upload_id,
150 | },
151 | {
152 | 'type': 'form-data',
153 | 'name': '_uuid',
154 | 'data': uuid,
155 | },
156 | {
157 | 'type': 'form-data',
158 | 'name': '_csrftoken',
159 | 'data': token,
160 | },
161 | {
162 | 'type': 'form-data',
163 | 'name': 'image_compression',
164 | 'data': '{"lib_name":"jt","lib_version":"1.3.0","quality":"70"}',
165 | },
166 | {
167 | 'type': 'form-data',
168 | 'name': 'photo',
169 | 'data': open(filepath, 'rb').read(),
170 | 'filename': 'pending_media_'+str(int((time.time()*1000)))+'.jpg',
171 | 'headers': ['Content-Transfer-Encoding: binary',
172 | 'Content-type: application/octet-stream'],
173 | },
174 | ]
175 |
176 | body = ''
177 | for b in bodies:
178 | body += '--' + boundary + '\r\n'
179 | body += 'Content-Disposition: ' + b['type'] + '; name="' + b['name'] + '"'
180 | if (b.get('filename', False)):
181 | # ext = pathinfo(b['filename'], PATHINFO_EXTENSION);
182 | body += '; filename="' + 'pending_media_' + str(int((time.time()*1000))) + '.jpg"'
183 | if (b.get('headers', False)):
184 | for header in b['headers']:
185 | body += "\r\n" + header
186 | body += "\r\n\r\n" + b['data'] + "\r\n"
187 | body += '--' + boundary + '--'
188 | return body
189 |
190 | def uploadPhoto(self, filepath, caption=None, upload_id=None):
191 | if not upload_id:
192 | upload_id = str(int((time.time()*1000)))
193 |
194 | data = self.buildBody(upload_id, self.uuid, self.token, filepath)
195 |
196 | endpoint = 'https://i.instagram.com/api/v1/upload/photo/'
197 |
198 | cookies = [c[0]+'='+c[1]+';' for c in self.s.cookies.items()]
199 | cookie_str = ' '.join(cookies)
200 |
201 | headers = {'Connection': 'close',
202 | 'Accept': '*/*',
203 | 'Content-type': 'multipart/form-data; boundary='+self.uuid,
204 | 'Content-Length': str(len(data)),
205 | 'Cookie': cookie_str,
206 | 'Cookie2': '$Version=1',
207 | 'Accept-Language': 'en-US',
208 | 'Accept-Encoding': 'gzip',
209 | 'User-Agent': self.USER_AGENT,
210 | }
211 |
212 | resp = self.s.post(endpoint, data=data, headers=headers)
213 | print json.loads(resp.text)
214 |
215 | self.expose()
216 | self.configure(upload_id, filepath, caption)
217 |
218 | return self.s.cookies
219 |
220 | def configure(self, upload_id, filepath, caption):
221 | im = Image.open(filepath)
222 | size = im.size
223 |
224 | post = json.dumps({
225 | 'upload_id': upload_id,
226 | 'camera_model': 'HM1S',
227 | 'source_type': 3,
228 | 'date_time_original': strftime("%Y:%m:%d %H:%M:%S"),
229 | 'camera_make': 'XIAOMI',
230 | 'edits': {
231 | 'crop_original_size': [size[0], size[1]],
232 | 'crop_zoom': 1.3333334,
233 | 'crop_center': [0.0, -0.0],
234 | },
235 | 'extra': {
236 | 'source_width': size[1],
237 | 'source_height': size[0],
238 | },
239 | 'device': {
240 | 'manufacturer': 'Xiaomi',
241 | 'model': 'HM 1SW',
242 | 'android_version': 18,
243 | 'android_release': '4.3',
244 | },
245 | '_csrftoken': self.token,
246 | '_uuid': self.uuid,
247 | '_uid': self.username_id,
248 | 'caption': caption,
249 | })
250 |
251 | post = post.replace('"crop_center":[0,0]', '"crop_center":[0.0,-0.0]')
252 |
253 | return self.SendRequest('media/configure/', self.generateSignature(post))
254 |
255 | # ###### API calls not used by Instegogram ####### #
256 |
257 | # def logout(self):
258 | # logout = self.SendRequest('accounts/logout/')
259 | # # TODO Instagram.php 180-185
260 |
261 | # def autoCompleteUserList(self):
262 | # return self.SendRequest('friendships/autocomplete_user_list/')
263 |
264 | # def timelineFeed(self):
265 | # return self.SendRequest('feed/timeline/')
266 |
267 | # def megaphoneLog(self):
268 | # return self.SendRequest('megaphone/log/')
269 |
270 | # def uploadVideo(self, video, caption = None):
271 | # # TODO Instagram.php 290-415
272 | # return False
273 |
274 | # def direct_share(self, media_id, recipients, text = None):
275 | # # TODO Instagram.php 420-490
276 | # return False
277 |
278 | # def configureVideo(upload_id, video, caption = ''):
279 | # # TODO Instagram.php 490-530
280 | # return False
281 |
282 | # def editMedia(self, mediaId, captionText = ''):
283 | # data = json.dumps({
284 | # '_uuid' : self.uuid,
285 | # '_uid' : self.username_id,
286 | # '_csrftoken' : self.token,
287 | # 'caption_text' : captionText
288 | # })
289 | # return self.SendRequest('media/'+ str(mediaId) +'/edit_media/', self.generateSignature(data))
290 |
291 | # def removeSelftag(self, mediaId):
292 | # data = json.dumps({
293 | # '_uuid' : self.uuid,
294 | # '_uid' : self.username_id,
295 | # '_csrftoken' : self.token
296 | # })
297 | # return self.SendRequest('media/'+ str(mediaId) +'/remove/', self.generateSignature(data))
298 |
299 | # def mediaInfo(self, mediaId):
300 | # data = json.dumps({
301 | # '_uuid' : self.uuid,
302 | # '_uid' : self.username_id,
303 | # '_csrftoken' : self.token,
304 | # 'media_id' : mediaId
305 | # })
306 | # return self.SendRequest('media/'+ str(mediaId) +'/info/', self.generateSignature(data))
307 |
308 | # def deleteMedia(self, mediaId):
309 | # data = json.dumps({
310 | # '_uuid' : self.uuid,
311 | # '_uid' : self.username_id,
312 | # '_csrftoken' : self.token,
313 | # 'media_id' : mediaId
314 | # })
315 | # return self.SendRequest('media/'+ str(mediaId) +'/delete/', self.generateSignature(data))
316 |
317 | # def comment(self, mediaId, commentText):
318 | # data = json.dumps({
319 | # '_uuid' : self.uuid,
320 | # '_uid' : self.username_id,
321 | # '_csrftoken' : self.token,
322 | # 'comment_text' : commentText
323 | # })
324 | # return self.SendRequest('media/'+ str(mediaId) +'/comment/', self.generateSignature(data))
325 |
326 | # def deleteComment(self, mediaId, captionText, commentId):
327 | # data = json.dumps({
328 | # '_uuid' : self.uuid,
329 | # '_uid' : self.username_id,
330 | # '_csrftoken' : self.token,
331 | # 'caption_text' : captionText
332 | # })
333 | # return self.SendRequest('media/'+ str(mediaId) +'/comment/'+ str(commentId) +'/delete/',
334 | # self.generateSignature(data))
335 |
336 | # def changeProfilePicture(self, photo):
337 | # # TODO Instagram.php 705-775
338 | # return False
339 |
340 | # def removeProfilePicture(self):
341 | # data = json.dumps({
342 | # '_uuid' : self.uuid,
343 | # '_uid' : self.username_id,
344 | # '_csrftoken' : self.token
345 | # })
346 | # return self.SendRequest('accounts/remove_profile_picture/', self.generateSignature(data))
347 |
348 | # def setPrivateAccount(self):
349 | # data = json.dumps({
350 | # '_uuid' : self.uuid,
351 | # '_uid' : self.username_id,
352 | # '_csrftoken' : self.token
353 | # })
354 | # return self.SendRequest('accounts/set_private/', self.generateSignature(data))
355 |
356 | # def setPublicAccount(self):
357 | # data = json.dumps({
358 | # '_uuid' : self.uuid,
359 | # '_uid' : self.username_id,
360 | # '_csrftoken' : self.token
361 | # })
362 | # return self.SendRequest('accounts/set_public/', self.generateSignature(data))
363 |
364 | # def getProfileData(self):
365 | # data = json.dumps({
366 | # '_uuid' : self.uuid,
367 | # '_uid' : self.username_id,
368 | # '_csrftoken' : self.token
369 | # })
370 | # return self.SendRequest('accounts/current_user/?edit=true', self.generateSignature(data))
371 |
372 | # def editProfile(self, url, phone, first_name, biography, email, gender):
373 | # data = json.dumps({
374 | # '_uuid' : self.uuid,
375 | # '_uid' : self.username_id,
376 | # '_csrftoken' : self.token,
377 | # 'external_url' : url,
378 | # 'phone_number' : phone,
379 | # 'username' : self.username,
380 | # 'full_name' : first_name,
381 | # 'biography' : biography,
382 | # 'email' : email,
383 | # 'gender' : gender,
384 | # })
385 | # return self.SendRequest('accounts/edit_profile/', self.generateSignature(data))
386 |
387 | # def getUsernameInfo(self, usernameId):
388 | # return self.SendRequest('users/'+ str(usernameId) +'/info/')
389 |
390 | # def getSelfUsernameInfo(self):
391 | # return self.getUsernameInfo(self.username_id)
392 |
393 | # def getRecentActivity(self):
394 | # activity = self.SendRequest('news/inbox/?')
395 | # # TODO Instagram.php 911-925
396 | # return activity
397 |
398 | # def getFollowingRecentActivity(self):
399 | # activity = self.SendRequest('news/?')
400 | # # TODO Instagram.php 935-945
401 | # return activity
402 |
403 | # def getv2Inbox(self):
404 | # inbox = self.SendRequest('direct_v2/inbox/?')
405 | # # TODO Instagram.php 950-960
406 | # return inbox
407 |
408 | # def getUserTags(self, usernameId):
409 | # tags = self.SendRequest('usertags/'+ str(usernameId) +'/feed/?rank_token='+ \
410 | # str(self.rank_token) +'&ranked_content=true&')
411 | # # TODO Instagram.php 975-985
412 | # return tags
413 |
414 | # def getSelfUserTags(self):
415 | # return self.getUserTags(self.username_id)
416 |
417 | # def tagFeed(self, tag):
418 | # userFeed = self.SendRequest('feed/tag/'+ str(tag) +'/?rank_token=' + \
419 | # str(self.rank_token) + '&ranked_content=true&')
420 | # # TODO Instagram.php 1000-1015
421 | # return userFeed
422 |
423 | # def getMediaLikers(self, mediaId):
424 | # likers = self.SendRequest('media/'+ str(mediaId) +'/likers/?')
425 | # # TODO Instagram.php 1025-1035
426 | # return likers
427 |
428 | # def getGeoMedia(self, usernameId):
429 | # locations = self.SendRequest('maps/user/'+ str(usernameId) +'/')
430 | # # TODO Instagram.php 1050-1060
431 | # return locations
432 |
433 | # def getSelfGeoMedia(self):
434 | # return self.getGeoMedia(self.username_id)
435 |
436 | # def fbUserSearch(self, query):
437 | # query = self.SendRequest('fbsearch/topsearch/?context=blended&query='+ str(query) + \
438 | # '&rank_token='+ str(self.rank_token))
439 | # # TODO Instagram.php 1080-1090
440 | # return query
441 |
442 | # def searchUsers(self, query):
443 | # query = self.SendRequest('users/search/?ig_sig_key_version='+ str(self.SIG_KEY_VERSION)
444 | # +'&is_typeahead=true&query='+ str(query) +'&rank_token='+ str(self.rank_token))
445 | # # TODO Instagram.php 1100-1110
446 | # return query
447 |
448 | # def searchUsername(self, usernameName):
449 | # query = self.SendRequest('users/'+ str(usernameName) +'/usernameinfo/')
450 | # # TODO Instagram.php 1080-1090
451 | # return query
452 |
453 | # def syncFromAdressBook(self, contacts):
454 | # return self.SendRequest('address_book/link/?include=extra_display_name,thumbnails', json.dumps(contacts))
455 |
456 | # def searchTags(self, query):
457 | # query = self.SendRequest('tags/search/?is_typeahead=true&q='+ str(query) + \
458 | # '&rank_token='+ str(self.rank_token))
459 | # # TODO Instagram.php 1160-1170
460 | # return query
461 |
462 | # def getTimeline(self):
463 | # query = self.SendRequest('feed/timeline/?rank_token='+ str(self.rank_token) +'&ranked_content=true&')
464 | # # TODO Instagram.php 1180-1190
465 | # return query
466 |
467 | # def getUserFeed(self, usernameId, maxid = '', minTimestamp = None):
468 | # # TODO Instagram.php 1200-1220
469 | # return False
470 |
471 | # def getSelfUserFeed(self):
472 | # return self.getUserFeed(self.username_id)
473 |
474 | # def getHashtagFeed(self, hashtagString, maxid = ''):
475 | # # TODO Instagram.php 1230-1250
476 | # return False
477 |
478 | # def searchLocation(self, query):
479 | # locationFeed = self.SendRequest('fbsearch/places/?rank_token='+ str(self.rank_token) +'&query=' + str(query))
480 | # # TODO Instagram.php 1250-1270
481 | # return locationFeed
482 |
483 | # def getLocationFeed(self, locationId, maxid = ''):
484 | # # TODO Instagram.php 1280-1300
485 | # return False
486 |
487 | # def getPopularFeed(self):
488 | # popularFeed = self.SendRequest('feed/popular/?people_teaser_supported=1&rank_token='+ \
489 | # str(self.rank_token) +'&ranked_content=true&')
490 | # # TODO Instagram.php 1315-1325
491 | # return popularFeed
492 |
493 | # def getUserFollowings(self, usernameId, maxid = ''):
494 | # return self.SendRequest('friendships/'+ str(usernameId) +'/following/?max_id='+ str(maxid)
495 | # +'&ig_sig_key_version='+ self.SIG_KEY_VERSION +'&rank_token='+ self.rank_token)
496 |
497 | # def getSelfUsersFollowing(self):
498 | # return self.getUserFollowings(self.username_id)
499 |
500 | # def getUserFollowers(self, usernameId, maxid = ''):
501 | # return self.SendRequest('friendships/'+ str(usernameId) +'/followers/?max_id='+ str(maxid)
502 | # +'&ig_sig_key_version='+ self.SIG_KEY_VERSION +'&rank_token='+ self.rank_token)
503 |
504 | # def getSelfUserFollowers(self):
505 | # return self.getUserFollowers(self.username_id)
506 |
507 | # def like(self, mediaId):
508 | # data = json.dumps({
509 | # '_uuid': self.uuid,
510 | # '_uid': self.username_id,
511 | # '_csrftoken': self.token,
512 | # 'media_id': mediaId
513 | # })
514 | # return self.SendRequest('media/' + str(mediaId) + '/like/', self.generateSignature(data))
515 |
516 | # def unlike(self, mediaId):
517 | # data = json.dumps({
518 | # '_uuid' : self.uuid,
519 | # '_uid' : self.username_id,
520 | # '_csrftoken' : self.token,
521 | # 'media_id' : mediaId
522 | # })
523 | # return self.SendRequest('media/'+ str(mediaId) +'/unlike/', self.generateSignature(data))
524 |
525 | # def getMediaComments(self, mediaId):
526 | # return self.SendRequest('media/'+ mediaId +'/comments/?')
527 |
528 | # def setNameAndPhone(self, name = '', phone = ''):
529 | # data = json.dumps({
530 | # '_uuid' : self.uuid,
531 | # '_uid' : self.username_id,
532 | # 'first_name' : name,
533 | # 'phone_number' : phone,
534 | # '_csrftoken' : self.token
535 | # })
536 | # return self.SendRequest('accounts/set_phone_and_name/', self.generateSignature(data))
537 |
538 | # def getDirectShare(self):
539 | # return self.SendRequest('direct_share/inbox/?')
540 |
541 | # def backup(self):
542 | # # TODO Instagram.php 1470-1485
543 | # return False
544 |
545 | # def follow(self, userId):
546 | # data = json.dumps({
547 | # '_uuid' : self.uuid,
548 | # '_uid' : self.username_id,
549 | # 'user_id' : userId,
550 | # '_csrftoken' : self.token
551 | # })
552 | # return self.SendRequest('friendships/create/'+ str(userId) +'/', self.generateSignature(data))
553 |
554 | # def unfollow(self, userId):
555 | # data = json.dumps({
556 | # '_uuid' : self.uuid,
557 | # '_uid' : self.username_id,
558 | # 'user_id' : userId,
559 | # '_csrftoken' : self.token
560 | # })
561 | # return self.SendRequest('friendships/destroy/'+ str(userId) +'/', self.generateSignature(data))
562 |
563 | # def block(self, userId):
564 | # data = json.dumps({
565 | # '_uuid' : self.uuid,
566 | # '_uid' : self.username_id,
567 | # 'user_id' : userId,
568 | # '_csrftoken' : self.token
569 | # })
570 | # return self.SendRequest('friendships/block/'+ str(userId) +'/', self.generateSignature(data))
571 |
572 | # def unblock(self, userId):
573 | # data = json.dumps({
574 | # '_uuid' : self.uuid,
575 | # '_uid' : self.username_id,
576 | # 'user_id' : userId,
577 | # '_csrftoken' : self.token
578 | # })
579 | # return self.SendRequest('friendships/unblock/'+ str(userId) +'/', self.generateSignature(data))
580 |
581 | # def userFriendship(self, userId):
582 | # data = json.dumps({
583 | # '_uuid' : self.uuid,
584 | # '_uid' : self.username_id,
585 | # 'user_id' : userId,
586 | # '_csrftoken' : self.token
587 | # })
588 | # return self.SendRequest('friendships/show/'+ str(userId) +'/', self.generateSignature(data))
589 |
590 | # def getLikedMedia(self):
591 | # return self.SendRequest('feed/liked/?')
592 |
593 | def generateSignature(self, data):
594 | post_string = 'ig_sig_key_version=' + self.SIG_KEY_VERSION + \
595 | '&signed_body=' + hmac.new(self.IG_SIG_KEY.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).hexdigest() + \
596 | '.' + urllib.quote(data)
597 | return post_string
598 |
599 | def generateDeviceId(self, seed):
600 | volatile_seed = "12345"
601 | m = hashlib.md5()
602 | m.update(seed.encode('utf-8') + volatile_seed.encode('utf-8'))
603 | return 'android-' + m.hexdigest()[:16]
604 |
605 | def generateUUID(self, type):
606 | uuid = '%04x%04x-%04x-%04x-%04x-%04x%04x%04x' % (random.randint(0, 0xffff),
607 | random.randint(0, 0xffff), random.randint(0, 0xffff),
608 | random.randint(0, 0x0fff) | 0x4000,
609 | random.randint(0, 0x3fff) | 0x8000,
610 | random.randint(0, 0xffff), random.randint(0, 0xffff),
611 | random.randint(0, 0xffff))
612 | if (type):
613 | return uuid
614 | else:
615 | return uuid.replace('-', '')
616 |
617 | def SendRequest(self, endpoint, post=None, login=False):
618 | # return self.SendRequest('media/upload/', self.generateSignature(data), post=photo)
619 | if (not self.isLoggedIn and not login):
620 | raise Exception("Not logged in!\n")
621 | return
622 |
623 | self.s.headers.update({'Connection': 'closed',
624 | 'Accept': '*/*',
625 | 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
626 | 'Cookie2': '$Version=1',
627 | 'Accept-Language': 'en-US',
628 | 'User-Agent': self.USER_AGENT})
629 |
630 | if (post): # POST
631 | response = self.s.post(self.API_URL + endpoint, post) # , verify=False
632 | else: # GET
633 | response = self.s.get(self.API_URL + endpoint) # , verify=False
634 |
635 | if response.status_code == 200:
636 | self.LastResponse = response
637 | self.LastJson = json.loads(response.text)
638 | return True
639 | else:
640 | print ("Request return " + str(response.status_code) + " error!")
641 | return False
642 |
--------------------------------------------------------------------------------