diff options
39 files changed, 956 insertions, 771 deletions
diff --git a/glyph_authlite.png b/Graphics/drawables/glyph_authlite.png Binary files differindex 1a40c4a2d..1a40c4a2d 100644 --- a/glyph_authlite.png +++ b/Graphics/drawables/glyph_authlite.png diff --git a/Graphics/drawables/material-launcher/vector-src.pdf b/Graphics/drawables/material-launcher/vector-src.pdf Binary files differnew file mode 100644 index 000000000..68807173d --- /dev/null +++ b/Graphics/drawables/material-launcher/vector-src.pdf diff --git a/Graphics/drawables/material-launcher/vector-src.svg b/Graphics/drawables/material-launcher/vector-src.svg index a4d255d48..975d085f2 100644 --- a/Graphics/drawables/material-launcher/vector-src.svg +++ b/Graphics/drawables/material-launcher/vector-src.svg @@ -1,213 +1,134 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="626px"
- height="626px" viewBox="0 0 626 626" enable-background="new 0 0 626 626" xml:space="preserve">
-<g id="Layer_9">
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#94C061" d="M313.25,252H265.3c1.047-0.933,2.105-1.85,3.175-2.75h44.775V252z
- M313.25,313v18.75h-80.5V292.3c1.324-2.403,2.724-4.778,4.2-7.125V313H313.25z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#658D38" d="M232.75,292.3v39.45h80.5V313h3.7v-61h-3.7v-2.75h-44.775
- c1.307-1.109,2.632-2.192,3.975-3.25c2.202-1.742,4.435-3.408,6.7-5c4.153-2.919,8.412-5.585,12.775-8
- c9.974-5.517,20.499-9.717,31.575-12.6c12.134-3.167,24.934-4.75,38.4-4.75c9.199,0,18.083,0.75,26.649,2.25c3,0.5,6,1.117,9,1.85
- c24.967,6,47.217,18.583,66.75,37.75c0.334,0.333,0.667,0.667,1,1c21.334,21.367,34.717,45.983,40.15,73.85
- c0.2,1.167,0.416,2.351,0.649,3.551c1.4,8.3,2.101,16.85,2.101,25.649c0,0.134,0,0.283,0,0.45c0,22.934-4.601,43.967-13.8,63.1
- c-4.601,9.434-10.284,18.417-17.051,26.95c-2,2.4-4.033,4.783-6.1,7.15c-1.934,2.133-3.917,4.217-5.95,6.25
- c-3.5,3.5-7.066,6.8-10.7,9.899c-4.833,4.034-9.833,7.717-15,11.051c-5.333,3.399-10.833,6.416-16.5,9.05
- c-0.533,0.2-1.033,0.416-1.5,0.649c-17.466,7.867-36.483,11.967-57.05,12.301c-0.533,0-1.05,0-1.55,0c-0.366,0-0.733,0-1.1,0
- c-40.434,0-74.934-14.317-103.5-42.95c-0.467-0.467-0.917-0.917-1.35-1.351c-27.7-28.3-41.55-62.333-41.55-102.1
- c0-0.167,0-0.316,0-0.45c0.042-14.928,2.042-29.053,6-42.375c0.517-1.738,1.067-3.464,1.65-5.175
- C225.71,306.504,228.91,299.271,232.75,292.3z"/>
-</g>
-<g id="Layer_6">
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#BBD89C" d="M304.3,301.2c-0.614-0.905-1.206-1.821-1.775-2.75
- c0.907-1.061,1.598-2.144,2.075-3.25c1.167-2.333,1.75-4.9,1.75-7.7c0-1.033-0.083-2.033-0.25-3
- c-0.368-2.332-1.21-4.482-2.525-6.45c0.112,0.118,0.204,0.209,0.275,0.275L304,278.5c2.9,3.256,4.35,7.09,4.35,11.5
- c0,1.759-0.233,3.425-0.7,5c-0.266,0.932-0.616,1.833-1.05,2.7C306.085,298.895,305.318,300.062,304.3,301.2z M296.825,306.425
- c-1.841,0.65-3.816,0.975-5.925,0.975c-2.68,0-5.146-0.533-7.4-1.6c-1.783-0.85-3.433-2.033-4.95-3.55
- c-0.894-0.895-1.669-1.836-2.325-2.825c0.106,0.106,0.214,0.214,0.325,0.325c3.434,3.433,7.55,5.15,12.35,5.15
- c2.171,0,4.205-0.35,6.1-1.05C295.588,304.717,296.197,305.575,296.825,306.425z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#7BAD45" d="M333.575,324.85c0.758-1.002,1.6-1.968,2.524-2.899
- c4.9-4.867,10.801-7.3,17.7-7.3c3.473,0,6.689,0.616,9.65,1.85c-0.393-0.553-0.81-1.095-1.25-1.625
- c0.193,0.112,0.385,0.229,0.575,0.35c0.349,0.262,0.69,0.537,1.024,0.825c1.32,1.517,2.354,3.133,3.101,4.851
- c0.1,0.199,0.199,0.383,0.3,0.55c1.1,2.033,1.866,4.184,2.3,6.45c0.063,0.356,0.121,0.715,0.175,1.074
- c-3.332,0.468-6.757,0.7-10.274,0.7C350.13,329.677,341.521,328.068,333.575,324.85z M436.125,303.05
- c-0.209-0.221-0.417-0.438-0.625-0.65c-1.628-1.637-3.294-3.195-5-4.675c2.639-5.218,4.613-10.71,5.925-16.475l25.851-25.775
- c0.342,0.343,0.684,0.685,1.024,1.025c21.334,21.367,34.717,45.983,40.15,73.85c0.2,1.167,0.416,2.351,0.649,3.551
- c1.4,8.3,2.101,16.85,2.101,25.649c0,0.134,0,0.283,0,0.45c0,22.934-4.601,43.967-13.8,63.1
- c-4.601,9.434-10.284,18.417-17.051,26.95c-0.075,0.091-0.15,0.183-0.225,0.275l5.975-21.976c0.101-0.399,0.051-0.816-0.149-1.25
- c-0.167-0.366-0.417-0.683-0.75-0.949c-0.134-0.034-8.167-8.784-24.101-26.25c3.448-7.858,5.157-14.851,5.125-20.976
- c-0.005-1.372-0.005-2.605,0-3.7c0.547-2.661,0.964-5.245,1.25-7.75c0.469-4.224,0.561-8.232,0.275-12.024
- c0.033-0.367,0.033-0.783,0-1.25c-0.4-2.634-0.884-5.25-1.45-7.851c-2.666-11.433-7.75-22.116-15.25-32.05
- c-1.333-1.7-2.7-3.383-4.1-5.05c-1.467-1.7-2.95-3.317-4.45-4.85C437.044,303.941,436.586,303.491,436.125,303.05z M255.05,462.1
- l46.725-46.574c1.695,2.331,3.52,4.623,5.475,6.875c0.874,0.999,1.757,1.975,2.65,2.925l51.075,81.075c-0.362,0-0.721,0-1.074,0
- c-40.434,0-74.934-14.317-103.5-42.95C255.947,462.998,255.498,462.548,255.05,462.1z M368.6,340.05
- c-1.316,3.908-3.649,7.324-7,10.25c-0.066,0.033-0.149,0.117-0.25,0.25c-0.1,0.033-0.149,0.084-0.149,0.15
- c-0.101,0.033-0.167,0.083-0.2,0.149c-0.434,0.367-0.866,0.733-1.3,1.101c-0.101,0.033-0.2,0.1-0.3,0.2
- c-2.434,1.8-4.9,3.017-7.4,3.649l-2.85,0.601c-0.267,0-0.517,0.033-0.75,0.1c-1,0.1-2.034,0.15-3.101,0.15c-0.1,0-0.2,0-0.3,0
- s-0.2,0-0.3,0c-0.101,0-0.2,0-0.3,0c-0.134-0.067-0.284-0.101-0.45-0.101h-0.25c-4.182-0.275-7.89-1.476-11.125-3.6
- c-2.517-3.904-3.775-8.338-3.775-13.3c0-2.092,0.226-4.092,0.675-6c9.717,4.448,20.358,6.682,31.926,6.699
- C363.842,340.347,366.242,340.246,368.6,340.05z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#94C061" d="M301.775,415.525L255.05,462.1
- c-27.7-28.304-41.55-62.337-41.55-102.1c0-0.167,0-0.316,0-0.45c0.089-36.006,11.589-67.339,34.5-94
- c1.905-2.214,3.888-4.397,5.95-6.55c0.805-0.837,1.622-1.671,2.45-2.5c4.5-4.5,9.184-8.667,14.05-12.5
- c0.165-0.13,0.332-0.264,0.5-0.4c0.336-0.264,0.669-0.522,1-0.775c0.154-0.119,0.304-0.235,0.45-0.35l13.15-8.95
- c0.752-0.458,1.511-0.908,2.275-1.35v0.025c0.41-0.238,0.818-0.479,1.225-0.725l1.225-0.675c0.469-0.25,0.935-0.5,1.4-0.75
- c4.524-2.416,9.158-4.565,13.9-6.45c4.045-1.601,8.171-3.009,12.375-4.225c1.177-0.34,2.36-0.665,3.55-0.975
- c12.141-3.167,24.94-4.75,38.4-4.75c9.199,0,18.083,0.75,26.649,2.25c3,0.5,6,1.117,9,1.85c7.557,1.815,14.865,4.231,21.925,7.25
- c6.836,10.689,10.252,22.79,10.25,36.3c0.002,10.708-2.14,20.524-6.425,29.45c-8.328-5.511-17.444-9.411-27.35-11.7
- c-0.233-0.066-0.45-0.117-0.65-0.15c-1.434-0.333-2.916-0.633-4.45-0.9c-0.199-0.033-0.35-0.05-0.449-0.05
- c-5.301-0.7-8.25-1.067-8.851-1.1h-0.05c-18.967-1.267-36.134,2.783-51.5,12.15c-4.233,2.567-8.316,5.55-12.25,8.95
- c-0.1,0.066-0.167,0.15-0.2,0.25c-0.133,0.1-0.283,0.233-0.45,0.4c-0.53,0.462-1.056,0.929-1.574,1.4
- c-2.051,1.869-3.984,3.803-5.8,5.8c-1.214-1.499-2.355-3.032-3.425-4.6c1.019-1.139,1.785-2.305,2.3-3.5
- c0.434-0.868,0.784-1.768,1.05-2.7c0.467-1.575,0.7-3.241,0.7-5c0-4.41-1.45-8.244-4.35-11.5l-0.15-0.175
- c-0.071-0.066-0.163-0.158-0.275-0.275c-0.069-0.076-0.144-0.159-0.225-0.25c-0.051-0.051-0.102-0.101-0.15-0.15
- c-0.633-0.633-1.317-1.217-2.05-1.75c-2.26-1.695-4.768-2.728-7.525-3.1H293.6c-0.432-0.1-0.898-0.149-1.4-0.15
- c-0.434-0.066-0.867-0.1-1.3-0.1c-0.333,0-0.65,0.034-0.95,0.1c-0.667,0-1.317,0.05-1.95,0.15h-0.05
- c-0.256,0.041-0.506,0.091-0.75,0.15l-2.2,0.6c-0.44,0.165-0.874,0.348-1.3,0.55c-0.117,0.05-0.233,0.1-0.35,0.15
- c-1.731,0.798-3.332,1.931-4.8,3.4c-0.2,0.2-0.383,0.417-0.55,0.65c-0.062,0.066-0.12,0.133-0.175,0.2
- c-1.904,2.117-3.179,4.5-3.825,7.15c-0.333,1.4-0.5,2.85-0.5,4.35c0,3.525,0.908,6.667,2.725,9.425
- c0.656,0.989,1.431,1.931,2.325,2.825c1.517,1.517,3.167,2.7,4.95,3.55c2.253,1.067,4.72,1.6,7.4,1.6
- c2.109,0,4.084-0.325,5.925-0.975c1.599,2.163,3.333,4.271,5.2,6.325c0.039-0.053,0.081-0.103,0.125-0.15
- c-0.357,0.478-0.708,0.961-1.05,1.45c-0.7,1-1.35,2-1.95,3c-1.6,2.434-3.083,4.967-4.45,7.601c-0.016,0.033-0.033,0.066-0.05,0.1
- c-1.014,2.101-1.964,4.217-2.85,6.35c-0.633,1.601-1.216,3.233-1.75,4.9c-2.267,6.967-3.683,14.384-4.25,22.25
- C284.278,379.587,289.603,398.679,301.775,415.525z M462.275,255.475l-25.851,25.775c1.307-5.716,1.966-11.699,1.976-17.95
- c-0.018-11.181-2.109-21.498-6.275-30.95C442.807,238.525,452.856,246.234,462.275,255.475z M362.2,314.875
- c-0.057-0.069-0.115-0.136-0.175-0.2c0.254,0.176,0.504,0.359,0.75,0.55C362.585,315.104,362.394,314.987,362.2,314.875z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#6C983D" d="M433,487.2c-3.879,2.285-7.846,4.368-11.9,6.25
- c-0.533,0.2-1.033,0.416-1.5,0.649c-17.453,7.864-36.47,11.956-57.05,12.275c-0.523,0.01-1.049,0.019-1.575,0.025L309.9,425.325
- c0.931,0.984,1.873,1.943,2.825,2.875c2.176,2.226,4.417,4.309,6.725,6.25c2.767,2.366,5.684,4.533,8.75,6.5
- c10.033,6.566,21.25,10.816,33.649,12.75c0.101,0,0.233,0,0.4,0c5.441-0.049,10.25-0.207,14.425-0.476
- c1.525-0.091,2.968-0.199,4.325-0.324c5.1-0.467,10.3-1.184,15.6-2.15l1.4,1.4v0.699c0.2-0.03,0.399-0.063,0.6-0.1L433,487.2z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#E2E2E2" d="M414.8,207.875c-14.804-14.35-32.604-21.525-53.399-21.525
- c-21.301,0-39.467,7.5-54.5,22.5c-3.536,3.543-6.652,7.26-9.35,11.15c-2.247,3.233-4.206,6.583-5.875,10.05
- c-0.465,0.25-0.931,0.5-1.4,0.75l-1.225,0.675c-0.406,0.246-0.815,0.487-1.225,0.725v-0.025
- c3.759-9.244,9.451-17.686,17.075-25.325c15.042-15.012,33.208-22.521,54.5-22.525c21.259,0.003,39.392,7.512,54.399,22.525
- C414.137,207.187,414.47,207.528,414.8,207.875z M408.7,213.95c0.331,0.331,0.664,0.665,1,1c3.316,3.316,6.225,6.799,8.725,10.45
- c7.55,11.081,11.325,23.714,11.325,37.9c0,10.513-2.066,20.171-6.2,28.975c-0.072,0.155-0.147,0.314-0.225,0.475
- c-0.525,1.09-1.084,2.165-1.675,3.225c-3.098,5.593-7.081,10.818-11.95,15.675c-4.908,4.92-10.191,8.937-15.851,12.05
- c-5.304,2.91-10.937,5.027-16.899,6.35c-1.729,0.384-3.486,0.7-5.275,0.95c-0.607,0.085-1.216,0.16-1.825,0.225
- c-2.757,0.317-5.573,0.476-8.449,0.476c-10.305,0-19.788-1.983-28.45-5.95c-2.097-0.97-4.146-2.053-6.15-3.25l-0.325,0.55
- c-0.111,0.182-0.22,0.365-0.324,0.55c0.322-0.596,0.673-1.18,1.05-1.75c2.078,1.124,4.203,2.124,6.375,3
- c7.946,3.219,16.555,4.827,25.825,4.825c3.518,0,6.942-0.232,10.274-0.7c0.526-0.073,1.052-0.156,1.575-0.25
- c1.565-0.254,3.106-0.562,4.625-0.925c11.948-2.813,22.557-8.863,31.825-18.15c5.637-5.626,10.078-11.743,13.325-18.35
- c0.096-0.186,0.188-0.369,0.274-0.55c4.285-8.926,6.427-18.742,6.425-29.45c0.002-13.51-3.414-25.61-10.25-36.3
- C414.997,221.133,412.072,217.45,408.7,213.95z M295,303.85c-6.065-8.954-9.948-18.82-11.65-29.6c0.117-0.05,0.233-0.1,0.35-0.15
- c0.426-0.203,0.859-0.386,1.3-0.55c1.337,10.945,4.862,20.97,10.575,30.075c0.549,0.882,1.115,1.757,1.7,2.625
- c0.852,1.233,1.744,2.45,2.675,3.65c0.707,0.909,1.44,1.809,2.2,2.7c-0.044,0.047-0.086,0.097-0.125,0.15
- c-1.867-2.054-3.601-4.163-5.2-6.325C296.197,305.575,295.588,304.717,295,303.85z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#D3D3D3" d="M305.575,223.6c-4.743,1.884-9.376,4.034-13.9,6.45
- c1.669-3.466,3.628-6.816,5.875-10.05c2.698-3.89,5.814-7.606,9.35-11.15c15.034-15,33.2-22.5,54.5-22.5
- c20.796,0,38.596,7.175,53.399,21.525c0.335,0.318,0.668,0.643,1,0.975c7.129,7.129,12.571,14.962,16.325,23.5
- c4.166,9.452,6.258,19.769,6.275,30.95c-0.01,6.251-0.669,12.234-1.976,17.95c-1.312,5.765-3.286,11.257-5.925,16.475
- c-0.037,0.074-0.07,0.148-0.1,0.225c-3.637,7.117-8.504,13.716-14.601,19.8c-10.662,10.686-22.903,17.577-36.725,20.675
- c-0.104,0.029-0.203,0.054-0.3,0.075c-2.47,0.547-4.986,0.972-7.551,1.275c-0.869,0.103-1.744,0.194-2.625,0.274
- c-2.357,0.196-4.758,0.297-7.199,0.3c-11.567-0.018-22.209-2.251-31.926-6.699c-1.929-0.879-3.821-1.846-5.675-2.9
- c0.047-0.277,0.098-0.552,0.15-0.825c0.015-0.092,0.031-0.184,0.05-0.274c0.173-0.857,0.39-1.69,0.65-2.5
- c0.388-1.194,0.871-2.336,1.449-3.426l0.051-0.125c0.104-0.185,0.213-0.368,0.324-0.55l0.325-0.55
- c2.004,1.197,4.054,2.28,6.15,3.25c8.662,3.967,18.146,5.95,28.45,5.95c2.876,0,5.692-0.158,8.449-0.476
- c0.609-0.064,1.218-0.14,1.825-0.225c1.789-0.25,3.547-0.566,5.275-0.95c5.963-1.322,11.596-3.439,16.899-6.35
- c5.659-3.113,10.942-7.13,15.851-12.05c4.869-4.857,8.853-10.082,11.95-15.675c0.591-1.061,1.149-2.135,1.675-3.225
- c0.077-0.161,0.152-0.32,0.225-0.475c4.134-8.804,6.2-18.462,6.2-28.975c0-14.186-3.775-26.819-11.325-37.9
- c-2.5-3.65-5.408-7.134-8.725-10.45c-0.336-0.335-0.669-0.669-1-1c-13.142-12.666-28.908-18.983-47.3-18.95
- c-18.9-0.033-35.034,6.617-48.4,19.95C310.24,217.717,307.766,220.6,305.575,223.6z M421.35,435.4l-3.1,2.449
- c-0.2,0.134-0.366,0.25-0.5,0.351c-0.267,0.2-0.517,0.366-0.75,0.5c-1.134,0.733-2.267,1.467-3.4,2.2
- c-0.1,0.033-0.183,0.083-0.25,0.149l65.601,65.75c0.133,0.134,0.316,0.316,0.55,0.55l1.1,1.101c1.4,1.366,3.067,2.05,5,2.05
- c1.634-0.066,3.051-0.55,4.25-1.45c0.301-0.166,0.551-0.383,0.75-0.649c0.233-0.167,1.25,0.399,3.051,1.699
- c1.767,1.334,5.116,4.817,10.05,10.45c4.866,5.533,11.833,9.533,20.899,12c-5.933,1-14.783,1.7-26.55,2.101
- c-11.121,0.349-19.721-2.201-25.8-7.65c-1.462-1.316-2.778-2.8-3.95-4.45l-20.1-20.1h0.05L433,487.2l-34.4-34.45l-0.6-0.6l-1.4-1.4
- c-5.3,0.967-10.5,1.684-15.6,2.15c-1.357,0.125-2.8,0.233-4.325,0.324l33.95-33.925c0.542,0.528,1.075,1.045,1.6,1.55
- c4.902,4.798,8.96,8.848,12.176,12.15c-0.167,0.066-0.284,0.167-0.351,0.3c-0.899,0.733-1.767,1.45-2.6,2.15L421.35,435.4z
- M302.15,312.6c-0.759-0.891-1.493-1.791-2.2-2.7c-0.932-1.2-1.823-2.417-2.675-3.65c-0.584-0.868-1.151-1.743-1.7-2.625
- c-5.713-9.105-9.238-19.13-10.575-30.075l2.2-0.6c0.244-0.06,0.494-0.109,0.75-0.15H288c0.633-0.1,1.283-0.15,1.95-0.15
- c0.3-0.066,0.617-0.1,0.95-0.1c0.434,0,0.867,0.034,1.3,0.1c0.501,0,0.968,0.05,1.4,0.15h0.025c0.946,7.487,3.054,14.487,6.325,21
- c0.793,1.581,1.651,3.131,2.575,4.65c0.569,0.929,1.161,1.845,1.775,2.75c1.07,1.567,2.211,3.101,3.425,4.6
- c-0.236,0.257-0.469,0.515-0.7,0.775L302.15,312.6z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#B6B6B6" d="M309.9,425.325c-0.893-0.95-1.776-1.926-2.65-2.925
- c-1.955-2.252-3.78-4.544-5.475-6.875c-12.172-16.847-17.497-35.938-15.975-57.275c0.567-7.866,1.983-15.283,4.25-22.25
- c0.534-1.667,1.117-3.3,1.75-4.9c0.886-2.133,1.836-4.249,2.85-6.35c0.017-0.033,0.034-0.066,0.05-0.1
- c1.367-2.634,2.85-5.167,4.45-7.601c0.6-1,1.25-2,1.95-3c0.342-0.488,0.692-0.972,1.05-1.45l4.875-6.025
- c0.231-0.26,0.464-0.519,0.7-0.775c1.816-1.997,3.75-3.931,5.8-5.8c0.519-0.471,1.044-0.938,1.574-1.4
- c0.167-0.167,0.317-0.3,0.45-0.4c0.033-0.1,0.101-0.184,0.2-0.25c3.934-3.4,8.017-6.383,12.25-8.95
- c15.366-9.367,32.533-13.417,51.5-12.15h0.05c0.601,0.033,3.55,0.4,8.851,1.1c0.1,0,0.25,0.017,0.449,0.05
- c1.534,0.267,3.017,0.567,4.45,0.9c0.2,0.033,0.417,0.083,0.65,0.15c9.905,2.289,19.021,6.189,27.35,11.7
- c-0.087,0.181-0.179,0.364-0.274,0.55c-7.707-4.751-16.065-8.167-25.075-10.25c-0.233-0.066-0.45-0.117-0.65-0.15
- c-1.434-0.333-2.916-0.633-4.45-0.9c-0.199-0.033-0.35-0.05-0.449-0.05c-5.301-0.7-8.25-1.067-8.851-1.1h-0.05
- c-18.98-1.281-36.146,2.769-51.5,12.15c-4.22,2.58-8.303,5.563-12.25,8.95c-0.1,0.066-0.167,0.15-0.2,0.25
- c-0.133,0.1-0.283,0.233-0.45,0.4c-5.433,4.733-10.1,9.883-14,15.45c-0.7,1-1.35,2-1.95,3c-1.6,2.434-3.083,4.967-4.45,7.601
- c-1.034,2.133-2,4.283-2.9,6.449c-0.634,1.608-1.217,3.242-1.75,4.9c-2.268,6.971-3.685,14.388-4.25,22.25
- c-1.451,20.351,3.324,38.659,14.325,54.925c2.144,3.148,4.519,6.224,7.125,9.226c0.08,0.091,0.163,0.183,0.25,0.274
- c1.057,1.209,2.132,2.384,3.225,3.525C311.773,427.269,310.831,426.31,309.9,425.325z M363.8,316.05
- c0.185,0.165,0.368,0.332,0.55,0.5c2.034,1.934,3.551,4.05,4.551,6.351c0.1,0.199,0.199,0.383,0.3,0.55
- c0.91,1.683,1.594,3.44,2.05,5.274c-0.523,0.094-1.049,0.177-1.575,0.25c-0.054-0.359-0.112-0.718-0.175-1.074
- c-0.434-2.267-1.2-4.417-2.3-6.45c-0.101-0.167-0.2-0.351-0.3-0.55C366.153,319.183,365.12,317.566,363.8,316.05z M329.9,350.9
- c0.853,0.762,1.744,1.445,2.675,2.05c3.235,2.124,6.943,3.324,11.125,3.6h0.25c0.166,0,0.316,0.033,0.45,0.101c0.1,0,0.199,0,0.3,0
- c0.1,0,0.2,0,0.3,0s0.2,0,0.3,0c1.066,0,2.101-0.051,3.101-0.15c0.233-0.066,0.483-0.1,0.75-0.1L352,355.8
- c2.5-0.633,4.967-1.85,7.4-3.649c0.1-0.101,0.199-0.167,0.3-0.2c0.434-0.367,0.866-0.733,1.3-1.101
- c0.033-0.066,0.1-0.116,0.2-0.149c0-0.066,0.05-0.117,0.149-0.15c0.101-0.133,0.184-0.217,0.25-0.25
- c3.351-2.926,5.684-6.342,7-10.25c0.881-0.08,1.756-0.172,2.625-0.274c-1.101,4.872-3.643,9.047-7.625,12.524
- c-0.066,0.033-0.149,0.117-0.25,0.25c-0.1,0.033-0.149,0.084-0.149,0.15c-0.101,0.033-0.167,0.083-0.2,0.149
- c-0.434,0.367-0.866,0.733-1.3,1.101c-0.101,0.033-0.2,0.1-0.3,0.2c-2.434,1.8-4.9,3.017-7.4,3.649l-2.85,0.601
- c-0.267,0-0.517,0.033-0.75,0.1c-1,0.1-2.034,0.15-3.101,0.15c-0.1,0-0.2,0-0.3,0s-0.2,0-0.3,0c-0.101,0-0.2,0-0.3,0
- c-0.134-0.067-0.284-0.101-0.45-0.101h-0.25c-5.009-0.33-9.343-1.98-13-4.95C331.72,352.795,330.787,351.895,329.9,350.9z
- M430.5,297.725c1.706,1.48,3.372,3.038,5,4.675c0.208,0.212,0.416,0.429,0.625,0.65c-1.864-1.801-3.772-3.501-5.725-5.1
- C430.43,297.874,430.463,297.799,430.5,297.725z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#A3A3A3" d="M462.475,367.475l-51.85,51.825l-33.95,33.925
- c-4.175,0.269-8.983,0.427-14.425,0.476c-0.167,0-0.3,0-0.4,0c-12.399-1.934-23.616-6.184-33.649-12.75
- c-3.066-1.967-5.983-4.134-8.75-6.5c-2.308-1.941-4.549-4.024-6.725-6.25c-1.093-1.142-2.168-2.316-3.225-3.525
- c-0.087-0.092-0.17-0.184-0.25-0.274c-2.606-3.002-4.981-6.077-7.125-9.226c-11.001-16.266-15.776-34.574-14.325-54.925
- c0.565-7.862,1.982-15.279,4.25-22.25c0.533-1.658,1.116-3.292,1.75-4.9c0.9-2.166,1.867-4.316,2.9-6.449
- c1.367-2.634,2.85-5.167,4.45-7.601c0.6-1,1.25-2,1.95-3c3.9-5.566,8.566-10.716,14-15.45c0.167-0.167,0.317-0.3,0.45-0.4
- c0.033-0.1,0.101-0.184,0.2-0.25c3.947-3.387,8.03-6.37,12.25-8.95c15.354-9.381,32.52-13.431,51.5-12.15h0.05
- c0.601,0.033,3.55,0.4,8.851,1.1c0.1,0,0.25,0.017,0.449,0.05c1.534,0.267,3.017,0.567,4.45,0.9c0.2,0.033,0.417,0.083,0.65,0.15
- c9.01,2.083,17.368,5.499,25.075,10.25c-3.247,6.607-7.688,12.724-13.325,18.35c-9.269,9.287-19.877,15.336-31.825,18.15
- c-1.519,0.363-3.06,0.671-4.625,0.925c-0.456-1.834-1.14-3.592-2.05-5.274c-0.101-0.167-0.2-0.351-0.3-0.55
- c-1-2.301-2.517-4.417-4.551-6.351c-0.182-0.168-0.365-0.335-0.55-0.5c-0.334-0.288-0.676-0.563-1.024-0.825
- c-0.246-0.19-0.496-0.374-0.75-0.55c-3.474-2.444-7.615-3.87-12.426-4.275c-2.301-0.139-4.501,0.011-6.6,0.45
- c-3.92,0.822-7.487,2.656-10.7,5.5c-0.233,0.167-0.45,0.351-0.649,0.551c-1.766,1.505-3.249,3.154-4.45,4.949
- c-0.377,0.57-0.728,1.154-1.05,1.75l-0.051,0.125c-0.578,1.09-1.062,2.231-1.449,3.426c-0.261,0.81-0.478,1.643-0.65,2.5
- c-0.019,0.091-0.035,0.183-0.05,0.274c-0.053,0.273-0.104,0.548-0.15,0.825c-0.114,0.75-0.198,1.517-0.25,2.3
- c0,0.033,0,0.084,0,0.15c-0.09,1.646-0.04,3.246,0.15,4.8c0.082,0.744,0.199,1.478,0.35,2.2c0.834,3.666,2.601,7.066,5.3,10.2
- c0.018,0.016,0.034,0.032,0.051,0.05h0.1c0.133,0.155,0.266,0.306,0.4,0.45c0.887,0.994,1.819,1.895,2.8,2.699
- c3.657,2.97,7.991,4.62,13,4.95h0.25c0.166,0,0.316,0.033,0.45,0.101c0.1,0,0.199,0,0.3,0c0.1,0,0.2,0,0.3,0s0.2,0,0.3,0
- c1.066,0,2.101-0.051,3.101-0.15c0.233-0.066,0.483-0.1,0.75-0.1L354,357.8c2.5-0.633,4.967-1.85,7.4-3.649
- c0.1-0.101,0.199-0.167,0.3-0.2c0.434-0.367,0.866-0.733,1.3-1.101c0.033-0.066,0.1-0.116,0.2-0.149c0-0.066,0.05-0.117,0.149-0.15
- c0.101-0.133,0.184-0.217,0.25-0.25c3.982-3.478,6.524-7.652,7.625-12.524c2.564-0.304,5.081-0.729,7.551-1.275
- c0.097-0.021,0.196-0.046,0.3-0.075c13.821-3.098,26.063-9.989,36.725-20.675c6.097-6.083,10.964-12.683,14.601-19.8
- c1.952,1.598,3.86,3.298,5.725,5.1c0.461,0.441,0.919,0.892,1.375,1.35c1.5,1.533,2.983,3.15,4.45,4.85
- c1.399,1.667,2.767,3.35,4.1,5.05c7.5,9.934,12.584,20.617,15.25,32.05c0.566,2.601,1.05,5.217,1.45,7.851
- c0.033,0.467,0.033,0.883,0,1.25C463.035,359.242,462.943,363.251,462.475,367.475z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#8F8F8F" d="M474.175,453.9h-0.024v0.05c-0.367,0.066-0.75,0.033-1.15-0.101
- c-0.192-0.096-0.359-0.229-0.5-0.399c-0.115-0.131-0.216-0.281-0.3-0.45v-0.15c-0.134-0.333-0.134-0.683,0-1.05l-0.05,0.101
- l6.949-25.551c0.101-0.399,0.051-0.816-0.149-1.25c-0.167-0.366-0.417-0.683-0.75-0.949c-0.134-0.034-8.167-8.784-24.101-26.25
- c1.967-3.834,4.051-10.117,6.25-18.851c0.32-1.295,0.612-2.57,0.875-3.825c-0.005,1.095-0.005,2.328,0,3.7
- c0.032,6.125-1.677,13.117-5.125,20.976c15.934,17.466,23.967,26.216,24.101,26.25c0.333,0.267,0.583,0.583,0.75,0.949
- c0.2,0.434,0.25,0.851,0.149,1.25l-5.975,21.976l-0.975,3.575l0.05-0.101C474.188,453.832,474.18,453.865,474.175,453.9z
- M527.95,473.35c6.564,5.904,9.681,14.588,9.35,26.051c-0.467,13.733-1.383,23.517-2.75,29.35c-0.233,1.167-0.767,2.25-1.6,3.25
- l-0.15,0.3c-0.333,0.233-0.649,0.45-0.95,0.65c-0.8,0.533-1.666,0.866-2.6,1c-0.8,0.2-1.684,0.399-2.65,0.6
- c-5.933,1-14.783,1.7-26.55,2.101c-12.402,0.389-21.669-2.827-27.8-9.65c6.079,5.449,14.679,7.999,25.8,7.65
- c11.767-0.4,20.617-1.101,26.55-2.101c0.967-0.2,1.851-0.399,2.65-0.6c0.934-0.134,1.8-0.467,2.6-1
- c0.301-0.2,0.617-0.417,0.95-0.65l0.15-0.3c0.833-1,1.366-2.083,1.6-3.25c1.367-5.833,2.283-15.616,2.75-29.35
- C535.596,487.171,533.146,479.154,527.95,473.35z M496.825,476.575c-0.357,0.039-0.698,0.014-1.025-0.075
- c-0.333-0.2-0.6-0.467-0.8-0.8c-0.066-0.066-0.1-0.15-0.1-0.25c-0.134-0.334-0.15-0.667-0.051-1l0.051-0.05l6.75-25.051
- c0.1-0.433,0.083-0.85-0.051-1.25c1.423,1.927,2.105,3.01,2.051,3.25L496.9,476.4l-0.051,0.05
- C496.838,476.489,496.83,476.531,496.825,476.575z M398.6,452.75c-0.2,0.036-0.399,0.069-0.6,0.1v-0.699L398.6,452.75z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#DBDBDB" d="M410.625,419.3l51.85-51.825c-0.286,2.505-0.703,5.089-1.25,7.75
- c-0.263,1.255-0.555,2.53-0.875,3.825c-2.199,8.733-4.283,15.017-6.25,18.851c15.934,17.466,23.967,26.216,24.101,26.25
- c0.333,0.267,0.583,0.583,0.75,0.949c0.2,0.434,0.25,0.851,0.149,1.25L472.15,451.9l0.05-0.101c-0.134,0.367-0.134,0.717,0,1.05
- V453c0.084,0.169,0.185,0.319,0.3,0.45c0.141,0.17,0.308,0.304,0.5,0.399c0.4,0.134,0.783,0.167,1.15,0.101v-0.05h0.024
- l25.325-6.65c0.434-0.233,0.833-0.217,1.2,0.05c0.333,0.167,0.633,0.434,0.899,0.8c0.134,0.4,0.15,0.817,0.051,1.25L494.9,474.4
- l-0.051,0.05c-0.1,0.333-0.083,0.666,0.051,1c0,0.1,0.033,0.184,0.1,0.25c0.2,0.333,0.467,0.6,0.8,0.8
- c0.327,0.089,0.668,0.114,1.025,0.075c0.039-0.01,0.081-0.018,0.125-0.025v0.101l0.1-0.101l26.65-7
- c1.576,1.142,2.992,2.408,4.25,3.8c5.195,5.805,7.646,13.821,7.35,24.051c-0.467,13.733-1.383,23.517-2.75,29.35
- c-0.233,1.167-0.767,2.25-1.6,3.25l-0.15,0.3c-0.333,0.233-0.649,0.45-0.95,0.65c-0.8,0.533-1.666,0.866-2.6,1
- c-0.8,0.2-1.684,0.399-2.65,0.6c-21.5-21.6-32.816-32.934-33.949-34L430.55,438.2c-0.2-0.101-0.366-0.233-0.5-0.4l-5.2-5.25
- l-0.149,0.15c-0.101,0.066-0.2,0.166-0.3,0.3c-3.216-3.303-7.273-7.353-12.176-12.15C411.7,420.345,411.167,419.828,410.625,419.3z
- M421.35,435.4l0.101,0.05h-0.101V435.4z"/>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#C8C8C8" d="M421.35,435.4v0.05h0.101c0.833-0.7,1.7-1.417,2.6-2.15
- c0.066-0.133,0.184-0.233,0.351-0.3c0.1-0.134,0.199-0.233,0.3-0.3l0.149-0.15l5.2,5.25c0.134,0.167,0.3,0.3,0.5,0.4l60.101,60.35
- c1.133,1.066,12.449,12.4,33.949,34c-9.066-2.467-16.033-6.467-20.899-12c-4.934-5.633-8.283-9.116-10.05-10.45
- c-1.801-1.3-2.817-1.866-3.051-1.699c-0.199,0.267-0.449,0.483-0.75,0.649c-1.199,0.9-2.616,1.384-4.25,1.45
- c-1.933,0-3.6-0.684-5-2.05l-1.1-1.101c-0.233-0.233-0.417-0.416-0.55-0.55l-65.601-65.75c0.067-0.066,0.15-0.116,0.25-0.149
- c1.134-0.733,2.267-1.467,3.4-2.2c0.233-0.134,0.483-0.3,0.75-0.5c0.134-0.101,0.3-0.217,0.5-0.351L421.35,435.4z"/>
-</g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + width="626px" + height="626px" + viewBox="0 0 626 626" + enable-background="new 0 0 626 626" + xml:space="preserve" + id="svg2" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="vector-src.svg"><metadata + id="metadata36"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs34" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2558" + inkscape:window-height="1419" + id="namedview32" + showgrid="false" + inkscape:zoom="0.266577" + inkscape:cx="-305.84541" + inkscape:cy="354.73238" + inkscape:window-x="0" + inkscape:window-y="19" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /> + +<g + id="g3013" + transform="translate(-20.480691,-1.8781185)"><g + transform="matrix(1.6991779,0,0,1.6991779,-304.41094,-297.68272)" + id="Layer_9"> + <path + style="fill:#94c061;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path5" + d="M 313.25,252 H 265.3 c 1.047,-0.933 2.105,-1.85 3.175,-2.75 H 313.25 V 252 z m 0,61 v 18.75 h -80.5 V 292.3 c 1.324,-2.403 2.724,-4.778 4.2,-7.125 V 313 h 76.3 z" + clip-rule="evenodd" /> + <path + style="fill:#658d38;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path7" + d="m 232.75,292.3 v 39.45 h 80.5 V 313 h 3.7 v -61 h -3.7 v -2.75 h -44.775 c 1.307,-1.109 2.632,-2.192 3.975,-3.25 2.202,-1.742 4.435,-3.408 6.7,-5 4.153,-2.919 8.412,-5.585 12.775,-8 9.974,-5.517 20.499,-9.717 31.575,-12.6 12.134,-3.167 24.934,-4.75 38.4,-4.75 9.199,0 18.083,0.75 26.649,2.25 3,0.5 6,1.117 9,1.85 24.967,6 47.217,18.583 66.75,37.75 0.334,0.333 0.667,0.667 1,1 21.334,21.367 34.717,45.983 40.15,73.85 0.2,1.167 0.416,2.351 0.649,3.551 1.4,8.3 2.101,16.85 2.101,25.649 0,0.134 0,0.283 0,0.45 0,22.934 -4.601,43.967 -13.8,63.1 -4.601,9.434 -10.284,18.417 -17.051,26.95 -2,2.4 -4.033,4.783 -6.1,7.15 -1.934,2.133 -3.917,4.217 -5.95,6.25 -3.5,3.5 -7.066,6.8 -10.7,9.899 -4.833,4.034 -9.833,7.717 -15,11.051 -5.333,3.399 -10.833,6.416 -16.5,9.05 -0.533,0.2 -1.033,0.416 -1.5,0.649 -17.466,7.867 -36.483,11.967 -57.05,12.301 -0.533,0 -1.05,0 -1.55,0 -0.366,0 -0.733,0 -1.1,0 -40.434,0 -74.934,-14.317 -103.5,-42.95 -0.467,-0.467 -0.917,-0.917 -1.35,-1.351 -27.7,-28.3 -41.55,-62.333 -41.55,-102.1 0,-0.167 0,-0.316 0,-0.45 0.042,-14.928 2.042,-29.053 6,-42.375 0.517,-1.738 1.067,-3.464 1.65,-5.175 2.562,-7.495 5.762,-14.728 9.602,-21.699 z" + clip-rule="evenodd" /> +</g><g + transform="matrix(1.6991779,0,0,1.6991779,-304.41094,-297.68272)" + id="Layer_6"> + <path + style="fill:#bbd89c;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path10" + d="m 304.3,301.2 c -0.614,-0.905 -1.206,-1.821 -1.775,-2.75 0.907,-1.061 1.598,-2.144 2.075,-3.25 1.167,-2.333 1.75,-4.9 1.75,-7.7 0,-1.033 -0.083,-2.033 -0.25,-3 -0.368,-2.332 -1.21,-4.482 -2.525,-6.45 0.112,0.118 0.204,0.209 0.275,0.275 L 304,278.5 c 2.9,3.256 4.35,7.09 4.35,11.5 0,1.759 -0.233,3.425 -0.7,5 -0.266,0.932 -0.616,1.833 -1.05,2.7 -0.515,1.195 -1.282,2.362 -2.3,3.5 z m -7.475,5.225 c -1.841,0.65 -3.816,0.975 -5.925,0.975 -2.68,0 -5.146,-0.533 -7.4,-1.6 -1.783,-0.85 -3.433,-2.033 -4.95,-3.55 -0.894,-0.895 -1.669,-1.836 -2.325,-2.825 0.106,0.106 0.214,0.214 0.325,0.325 3.434,3.433 7.55,5.15 12.35,5.15 2.171,0 4.205,-0.35 6.1,-1.05 0.588,0.867 1.197,1.725 1.825,2.575 z" + clip-rule="evenodd" /> + <path + style="fill:#7bad45;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path12" + d="m 333.575,324.85 c 0.758,-1.002 1.6,-1.968 2.524,-2.899 4.9,-4.867 10.801,-7.3 17.7,-7.3 3.473,0 6.689,0.616 9.65,1.85 -0.393,-0.553 -0.81,-1.095 -1.25,-1.625 0.193,0.112 0.385,0.229 0.575,0.35 0.349,0.262 0.69,0.537 1.024,0.825 1.32,1.517 2.354,3.133 3.101,4.851 0.1,0.199 0.199,0.383 0.3,0.55 1.1,2.033 1.866,4.184 2.3,6.45 0.063,0.356 0.121,0.715 0.175,1.074 -3.332,0.468 -6.757,0.7 -10.274,0.7 -9.27,0.001 -17.879,-1.608 -25.825,-4.826 z m 102.55,-21.8 c -0.209,-0.221 -0.417,-0.438 -0.625,-0.65 -1.628,-1.637 -3.294,-3.195 -5,-4.675 2.639,-5.218 4.613,-10.71 5.925,-16.475 l 25.851,-25.775 c 0.342,0.343 0.684,0.685 1.024,1.025 21.334,21.367 34.717,45.983 40.15,73.85 0.2,1.167 0.416,2.351 0.649,3.551 1.4,8.3 2.101,16.85 2.101,25.649 0,0.134 0,0.283 0,0.45 0,22.934 -4.601,43.967 -13.8,63.1 -4.601,9.434 -10.284,18.417 -17.051,26.95 -0.075,0.091 -0.15,0.183 -0.225,0.275 l 5.975,-21.976 c 0.101,-0.399 0.051,-0.816 -0.149,-1.25 -0.167,-0.366 -0.417,-0.683 -0.75,-0.949 -0.134,-0.034 -8.167,-8.784 -24.101,-26.25 3.448,-7.858 5.157,-14.851 5.125,-20.976 -0.005,-1.372 -0.005,-2.605 0,-3.7 0.547,-2.661 0.964,-5.245 1.25,-7.75 0.469,-4.224 0.561,-8.232 0.275,-12.024 0.033,-0.367 0.033,-0.783 0,-1.25 -0.4,-2.634 -0.884,-5.25 -1.45,-7.851 -2.666,-11.433 -7.75,-22.116 -15.25,-32.05 -1.333,-1.7 -2.7,-3.383 -4.1,-5.05 -1.467,-1.7 -2.95,-3.317 -4.45,-4.85 -0.455,-0.458 -0.913,-0.908 -1.374,-1.349 z M 255.05,462.1 301.775,415.526 c 1.695,2.331 3.52,4.623 5.475,6.875 0.874,0.999 1.757,1.975 2.65,2.925 l 51.075,81.075 c -0.362,0 -0.721,0 -1.074,0 -40.434,0 -74.934,-14.317 -103.5,-42.95 -0.454,-0.453 -0.903,-0.903 -1.351,-1.351 z M 368.6,340.05 c -1.316,3.908 -3.649,7.324 -7,10.25 -0.066,0.033 -0.149,0.117 -0.25,0.25 -0.1,0.033 -0.149,0.084 -0.149,0.15 -0.101,0.033 -0.167,0.083 -0.2,0.149 -0.434,0.367 -0.866,0.733 -1.3,1.101 -0.101,0.033 -0.2,0.1 -0.3,0.2 -2.434,1.8 -4.9,3.017 -7.4,3.649 l -2.85,0.601 c -0.267,0 -0.517,0.033 -0.75,0.1 -1,0.1 -2.034,0.15 -3.101,0.15 -0.1,0 -0.2,0 -0.3,0 -0.1,0 -0.2,0 -0.3,0 -0.101,0 -0.2,0 -0.3,0 -0.134,-0.067 -0.284,-0.101 -0.45,-0.101 h -0.25 c -4.182,-0.275 -7.89,-1.476 -11.125,-3.6 -2.517,-3.904 -3.775,-8.338 -3.775,-13.3 0,-2.092 0.226,-4.092 0.675,-6 9.717,4.448 20.358,6.682 31.926,6.699 2.441,-0.001 4.841,-0.102 7.199,-0.298 z" + clip-rule="evenodd" /> + <path + style="fill:#94c061;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path14" + d="M 301.775,415.525 255.05,462.1 C 227.35,433.796 213.5,399.763 213.5,360 c 0,-0.167 0,-0.316 0,-0.45 0.089,-36.006 11.589,-67.339 34.5,-94 1.905,-2.214 3.888,-4.397 5.95,-6.55 0.805,-0.837 1.622,-1.671 2.45,-2.5 4.5,-4.5 9.184,-8.667 14.05,-12.5 0.165,-0.13 0.332,-0.264 0.5,-0.4 0.336,-0.264 0.669,-0.522 1,-0.775 0.154,-0.119 0.304,-0.235 0.45,-0.35 l 13.15,-8.95 c 0.752,-0.458 1.511,-0.908 2.275,-1.35 v 0.025 c 0.41,-0.238 0.818,-0.479 1.225,-0.725 l 1.225,-0.675 c 0.469,-0.25 0.935,-0.5 1.4,-0.75 4.524,-2.416 9.158,-4.565 13.9,-6.45 4.045,-1.601 8.171,-3.009 12.375,-4.225 1.177,-0.34 2.36,-0.665 3.55,-0.975 12.141,-3.167 24.94,-4.75 38.4,-4.75 9.199,0 18.083,0.75 26.649,2.25 3,0.5 6,1.117 9,1.85 7.557,1.815 14.865,4.231 21.925,7.25 6.836,10.689 10.252,22.79 10.25,36.3 0.002,10.708 -2.14,20.524 -6.425,29.45 -8.328,-5.511 -17.444,-9.411 -27.35,-11.7 -0.233,-0.066 -0.45,-0.117 -0.65,-0.15 -1.434,-0.333 -2.916,-0.633 -4.45,-0.9 -0.199,-0.033 -0.35,-0.05 -0.449,-0.05 -5.301,-0.7 -8.25,-1.067 -8.851,-1.1 h -0.05 c -18.967,-1.267 -36.134,2.783 -51.5,12.15 -4.233,2.567 -8.316,5.55 -12.25,8.95 -0.1,0.066 -0.167,0.15 -0.2,0.25 -0.133,0.1 -0.283,0.233 -0.45,0.4 -0.53,0.462 -1.056,0.929 -1.574,1.4 -2.051,1.869 -3.984,3.803 -5.8,5.8 -1.214,-1.499 -2.355,-3.032 -3.425,-4.6 1.019,-1.139 1.785,-2.305 2.3,-3.5 0.434,-0.868 0.784,-1.768 1.05,-2.7 0.467,-1.575 0.7,-3.241 0.7,-5 0,-4.41 -1.45,-8.244 -4.35,-11.5 l -0.15,-0.175 c -0.071,-0.066 -0.163,-0.158 -0.275,-0.275 -0.069,-0.076 -0.144,-0.159 -0.225,-0.25 -0.051,-0.051 -0.102,-0.101 -0.15,-0.15 -0.633,-0.633 -1.317,-1.217 -2.05,-1.75 -2.26,-1.695 -4.768,-2.728 -7.525,-3.1 H 293.6 c -0.432,-0.1 -0.898,-0.149 -1.4,-0.15 -0.434,-0.066 -0.867,-0.1 -1.3,-0.1 -0.333,0 -0.65,0.034 -0.95,0.1 -0.667,0 -1.317,0.05 -1.95,0.15 h -0.05 c -0.256,0.041 -0.506,0.091 -0.75,0.15 l -2.2,0.6 c -0.44,0.165 -0.874,0.348 -1.3,0.55 -0.117,0.05 -0.233,0.1 -0.35,0.15 -1.731,0.798 -3.332,1.931 -4.8,3.4 -0.2,0.2 -0.383,0.417 -0.55,0.65 -0.062,0.066 -0.12,0.133 -0.175,0.2 -1.904,2.117 -3.179,4.5 -3.825,7.15 -0.333,1.4 -0.5,2.85 -0.5,4.35 0,3.525 0.908,6.667 2.725,9.425 0.656,0.989 1.431,1.931 2.325,2.825 1.517,1.517 3.167,2.7 4.95,3.55 2.253,1.067 4.72,1.6 7.4,1.6 2.109,0 4.084,-0.325 5.925,-0.975 1.599,2.163 3.333,4.271 5.2,6.325 0.039,-0.053 0.081,-0.103 0.125,-0.15 -0.357,0.478 -0.708,0.961 -1.05,1.45 -0.7,1 -1.35,2 -1.95,3 -1.6,2.434 -3.083,4.967 -4.45,7.601 -0.016,0.033 -0.033,0.066 -0.05,0.1 -1.014,2.101 -1.964,4.217 -2.85,6.35 -0.633,1.601 -1.216,3.233 -1.75,4.9 -2.267,6.967 -3.683,14.384 -4.25,22.25 -1.522,21.336 3.803,40.428 15.975,57.274 z m 160.5,-160.05 -25.851,25.775 c 1.307,-5.716 1.966,-11.699 1.976,-17.95 -0.018,-11.181 -2.109,-21.498 -6.275,-30.95 10.682,6.175 20.731,13.884 30.15,23.125 z m -100.075,59.4 c -0.057,-0.069 -0.115,-0.136 -0.175,-0.2 0.254,0.176 0.504,0.359 0.75,0.55 -0.19,-0.121 -0.381,-0.238 -0.575,-0.35 z" + clip-rule="evenodd" /> + <path + style="fill:#6c983d;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path16" + d="m 433,487.2 c -3.879,2.285 -7.846,4.368 -11.9,6.25 -0.533,0.2 -1.033,0.416 -1.5,0.649 -17.453,7.864 -36.47,11.956 -57.05,12.275 -0.523,0.01 -1.049,0.019 -1.575,0.025 L 309.9,425.325 c 0.931,0.984 1.873,1.943 2.825,2.875 2.176,2.226 4.417,4.309 6.725,6.25 2.767,2.366 5.684,4.533 8.75,6.5 10.033,6.566 21.25,10.816 33.649,12.75 0.101,0 0.233,0 0.4,0 5.441,-0.049 10.25,-0.207 14.425,-0.476 1.525,-0.091 2.968,-0.199 4.325,-0.324 5.1,-0.467 10.3,-1.184 15.6,-2.15 l 1.4,1.4 v 0.699 c 0.2,-0.03 0.399,-0.063 0.6,-0.1 L 433,487.2 z" + clip-rule="evenodd" /> + <path + style="fill:#e2e2e2;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path18" + d="m 414.8,207.875 c -14.804,-14.35 -32.604,-21.525 -53.399,-21.525 -21.301,0 -39.467,7.5 -54.5,22.5 -3.536,3.543 -6.652,7.26 -9.35,11.15 -2.247,3.233 -4.206,6.583 -5.875,10.05 -0.465,0.25 -0.931,0.5 -1.4,0.75 l -1.225,0.675 c -0.406,0.246 -0.815,0.487 -1.225,0.725 v -0.025 c 3.759,-9.244 9.451,-17.686 17.075,-25.325 15.042,-15.012 33.208,-22.521 54.5,-22.525 21.259,0.003 39.392,7.512 54.399,22.525 0.337,0.337 0.67,0.678 1,1.025 z m -6.1,6.075 c 0.331,0.331 0.664,0.665 1,1 3.316,3.316 6.225,6.799 8.725,10.45 7.55,11.081 11.325,23.714 11.325,37.9 0,10.513 -2.066,20.171 -6.2,28.975 -0.072,0.155 -0.147,0.314 -0.225,0.475 -0.525,1.09 -1.084,2.165 -1.675,3.225 -3.098,5.593 -7.081,10.818 -11.95,15.675 -4.908,4.92 -10.191,8.937 -15.851,12.05 -5.304,2.91 -10.937,5.027 -16.899,6.35 -1.729,0.384 -3.486,0.7 -5.275,0.95 -0.607,0.085 -1.216,0.16 -1.825,0.225 -2.757,0.317 -5.573,0.476 -8.449,0.476 -10.305,0 -19.788,-1.983 -28.45,-5.95 -2.097,-0.97 -4.146,-2.053 -6.15,-3.25 l -0.325,0.55 c -0.111,0.182 -0.22,0.365 -0.324,0.55 0.322,-0.596 0.673,-1.18 1.05,-1.75 2.078,1.124 4.203,2.124 6.375,3 7.946,3.219 16.555,4.827 25.825,4.825 3.518,0 6.942,-0.232 10.274,-0.7 0.526,-0.073 1.052,-0.156 1.575,-0.25 1.565,-0.254 3.106,-0.562 4.625,-0.925 11.948,-2.813 22.557,-8.863 31.825,-18.15 5.637,-5.626 10.078,-11.743 13.325,-18.35 0.096,-0.186 0.188,-0.369 0.274,-0.55 4.285,-8.926 6.427,-18.742 6.425,-29.45 0.002,-13.51 -3.414,-25.61 -10.25,-36.3 -2.478,-3.868 -5.403,-7.551 -8.775,-11.051 z M 295,303.85 c -6.065,-8.954 -9.948,-18.82 -11.65,-29.6 0.117,-0.05 0.233,-0.1 0.35,-0.15 0.426,-0.203 0.859,-0.386 1.3,-0.55 1.337,10.945 4.862,20.97 10.575,30.075 0.549,0.882 1.115,1.757 1.7,2.625 0.852,1.233 1.744,2.45 2.675,3.65 0.707,0.909 1.44,1.809 2.2,2.7 -0.044,0.047 -0.086,0.097 -0.125,0.15 -1.867,-2.054 -3.601,-4.163 -5.2,-6.325 -0.628,-0.85 -1.237,-1.708 -1.825,-2.575 z" + clip-rule="evenodd" /> + <path + style="fill:#d3d3d3;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path20" + d="m 305.575,223.6 c -4.743,1.884 -9.376,4.034 -13.9,6.45 1.669,-3.466 3.628,-6.816 5.875,-10.05 2.698,-3.89 5.814,-7.606 9.35,-11.15 15.034,-15 33.2,-22.5 54.5,-22.5 20.796,0 38.596,7.175 53.399,21.525 0.335,0.318 0.668,0.643 1,0.975 7.129,7.129 12.571,14.962 16.325,23.5 4.166,9.452 6.258,19.769 6.275,30.95 -0.01,6.251 -0.669,12.234 -1.976,17.95 -1.312,5.765 -3.286,11.257 -5.925,16.475 -0.037,0.074 -0.07,0.148 -0.1,0.225 -3.637,7.117 -8.504,13.716 -14.601,19.8 -10.662,10.686 -22.903,17.577 -36.725,20.675 -0.104,0.029 -0.203,0.054 -0.3,0.075 -2.47,0.547 -4.986,0.972 -7.551,1.275 -0.869,0.103 -1.744,0.194 -2.625,0.274 -2.357,0.196 -4.758,0.297 -7.199,0.3 -11.567,-0.018 -22.209,-2.251 -31.926,-6.699 -1.929,-0.879 -3.821,-1.846 -5.675,-2.9 0.047,-0.277 0.098,-0.552 0.15,-0.825 0.015,-0.092 0.031,-0.184 0.05,-0.274 0.173,-0.857 0.39,-1.69 0.65,-2.5 0.388,-1.194 0.871,-2.336 1.449,-3.426 l 0.051,-0.125 c 0.104,-0.185 0.213,-0.368 0.324,-0.55 l 0.325,-0.55 c 2.004,1.197 4.054,2.28 6.15,3.25 8.662,3.967 18.146,5.95 28.45,5.95 2.876,0 5.692,-0.158 8.449,-0.476 0.609,-0.064 1.218,-0.14 1.825,-0.225 1.789,-0.25 3.547,-0.566 5.275,-0.95 5.963,-1.322 11.596,-3.439 16.899,-6.35 5.659,-3.113 10.942,-7.13 15.851,-12.05 4.869,-4.857 8.853,-10.082 11.95,-15.675 0.591,-1.061 1.149,-2.135 1.675,-3.225 0.077,-0.161 0.152,-0.32 0.225,-0.475 4.134,-8.804 6.2,-18.462 6.2,-28.975 0,-14.186 -3.775,-26.819 -11.325,-37.9 -2.5,-3.65 -5.408,-7.134 -8.725,-10.45 -0.336,-0.335 -0.669,-0.669 -1,-1 -13.142,-12.666 -28.908,-18.983 -47.3,-18.95 -18.9,-0.033 -35.034,6.617 -48.4,19.95 -2.754,2.768 -5.228,5.651 -7.419,8.651 z m 115.775,211.8 -3.1,2.449 c -0.2,0.134 -0.366,0.25 -0.5,0.351 -0.267,0.2 -0.517,0.366 -0.75,0.5 -1.134,0.733 -2.267,1.467 -3.4,2.2 -0.1,0.033 -0.183,0.083 -0.25,0.149 l 65.601,65.75 c 0.133,0.134 0.316,0.316 0.55,0.55 l 1.1,1.101 c 1.4,1.366 3.067,2.05 5,2.05 1.634,-0.066 3.051,-0.55 4.25,-1.45 0.301,-0.166 0.551,-0.383 0.75,-0.649 0.233,-0.167 1.25,0.399 3.051,1.699 1.767,1.334 5.116,4.817 10.05,10.45 4.866,5.533 11.833,9.533 20.899,12 -5.933,1 -14.783,1.7 -26.55,2.101 -11.121,0.349 -19.721,-2.201 -25.8,-7.65 -1.462,-1.316 -2.778,-2.8 -3.95,-4.45 l -20.1,-20.1 h 0.05 L 433,487.2 l -34.4,-34.45 -0.6,-0.6 -1.4,-1.4 c -5.3,0.967 -10.5,1.684 -15.6,2.15 -1.357,0.125 -2.8,0.233 -4.325,0.324 l 33.95,-33.925 c 0.542,0.528 1.075,1.045 1.6,1.55 4.902,4.798 8.96,8.848 12.176,12.15 -0.167,0.066 -0.284,0.167 -0.351,0.3 -0.899,0.733 -1.767,1.45 -2.6,2.15 l -0.1,-0.049 z M 302.15,312.6 c -0.759,-0.891 -1.493,-1.791 -2.2,-2.7 -0.932,-1.2 -1.823,-2.417 -2.675,-3.65 -0.584,-0.868 -1.151,-1.743 -1.7,-2.625 -5.713,-9.105 -9.238,-19.13 -10.575,-30.075 l 2.2,-0.6 c 0.244,-0.06 0.494,-0.109 0.75,-0.15 H 288 c 0.633,-0.1 1.283,-0.15 1.95,-0.15 0.3,-0.066 0.617,-0.1 0.95,-0.1 0.434,0 0.867,0.034 1.3,0.1 0.501,0 0.968,0.05 1.4,0.15 h 0.025 c 0.946,7.487 3.054,14.487 6.325,21 0.793,1.581 1.651,3.131 2.575,4.65 0.569,0.929 1.161,1.845 1.775,2.75 1.07,1.567 2.211,3.101 3.425,4.6 -0.236,0.257 -0.469,0.515 -0.7,0.775 l -4.875,6.025 z" + clip-rule="evenodd" /> + <path + style="fill:#b6b6b6;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path22" + d="m 309.9,425.325 c -0.893,-0.95 -1.776,-1.926 -2.65,-2.925 -1.955,-2.252 -3.78,-4.544 -5.475,-6.875 -12.172,-16.847 -17.497,-35.938 -15.975,-57.275 0.567,-7.866 1.983,-15.283 4.25,-22.25 0.534,-1.667 1.117,-3.3 1.75,-4.9 0.886,-2.133 1.836,-4.249 2.85,-6.35 0.017,-0.033 0.034,-0.066 0.05,-0.1 1.367,-2.634 2.85,-5.167 4.45,-7.601 0.6,-1 1.25,-2 1.95,-3 0.342,-0.488 0.692,-0.972 1.05,-1.45 l 4.875,-6.025 c 0.231,-0.26 0.464,-0.519 0.7,-0.775 1.816,-1.997 3.75,-3.931 5.8,-5.8 0.519,-0.471 1.044,-0.938 1.574,-1.4 0.167,-0.167 0.317,-0.3 0.45,-0.4 0.033,-0.1 0.101,-0.184 0.2,-0.25 3.934,-3.4 8.017,-6.383 12.25,-8.95 15.366,-9.367 32.533,-13.417 51.5,-12.15 h 0.05 c 0.601,0.033 3.55,0.4 8.851,1.1 0.1,0 0.25,0.017 0.449,0.05 1.534,0.267 3.017,0.567 4.45,0.9 0.2,0.033 0.417,0.083 0.65,0.15 9.905,2.289 19.021,6.189 27.35,11.7 -0.087,0.181 -0.179,0.364 -0.274,0.55 -7.707,-4.751 -16.065,-8.167 -25.075,-10.25 -0.233,-0.066 -0.45,-0.117 -0.65,-0.15 -1.434,-0.333 -2.916,-0.633 -4.45,-0.9 -0.199,-0.033 -0.35,-0.05 -0.449,-0.05 -5.301,-0.7 -8.25,-1.067 -8.851,-1.1 h -0.05 c -18.98,-1.281 -36.146,2.769 -51.5,12.15 -4.22,2.58 -8.303,5.563 -12.25,8.95 -0.1,0.066 -0.167,0.15 -0.2,0.25 -0.133,0.1 -0.283,0.233 -0.45,0.4 -5.433,4.733 -10.1,9.883 -14,15.45 -0.7,1 -1.35,2 -1.95,3 -1.6,2.434 -3.083,4.967 -4.45,7.601 -1.034,2.133 -2,4.283 -2.9,6.449 -0.634,1.608 -1.217,3.242 -1.75,4.9 -2.268,6.971 -3.685,14.388 -4.25,22.25 -1.451,20.351 3.324,38.659 14.325,54.925 2.144,3.148 4.519,6.224 7.125,9.226 0.08,0.091 0.163,0.183 0.25,0.274 1.057,1.209 2.132,2.384 3.225,3.525 -0.952,-0.93 -1.894,-1.889 -2.825,-2.874 z M 363.8,316.05 c 0.185,0.165 0.368,0.332 0.55,0.5 2.034,1.934 3.551,4.05 4.551,6.351 0.1,0.199 0.199,0.383 0.3,0.55 0.91,1.683 1.594,3.44 2.05,5.274 -0.523,0.094 -1.049,0.177 -1.575,0.25 -0.054,-0.359 -0.112,-0.718 -0.175,-1.074 -0.434,-2.267 -1.2,-4.417 -2.3,-6.45 -0.101,-0.167 -0.2,-0.351 -0.3,-0.55 -0.748,-1.718 -1.781,-3.335 -3.101,-4.851 z m -33.9,34.85 c 0.853,0.762 1.744,1.445 2.675,2.05 3.235,2.124 6.943,3.324 11.125,3.6 h 0.25 c 0.166,0 0.316,0.033 0.45,0.101 0.1,0 0.199,0 0.3,0 0.1,0 0.2,0 0.3,0 0.1,0 0.2,0 0.3,0 1.066,0 2.101,-0.051 3.101,-0.15 0.233,-0.066 0.483,-0.1 0.75,-0.1 L 352,355.8 c 2.5,-0.633 4.967,-1.85 7.4,-3.649 0.1,-0.101 0.199,-0.167 0.3,-0.2 0.434,-0.367 0.866,-0.733 1.3,-1.101 0.033,-0.066 0.1,-0.116 0.2,-0.149 0,-0.066 0.05,-0.117 0.149,-0.15 0.101,-0.133 0.184,-0.217 0.25,-0.25 3.351,-2.926 5.684,-6.342 7,-10.25 0.881,-0.08 1.756,-0.172 2.625,-0.274 -1.101,4.872 -3.643,9.047 -7.625,12.524 -0.066,0.033 -0.149,0.117 -0.25,0.25 -0.1,0.033 -0.149,0.084 -0.149,0.15 -0.101,0.033 -0.167,0.083 -0.2,0.149 -0.434,0.367 -0.866,0.733 -1.3,1.101 -0.101,0.033 -0.2,0.1 -0.3,0.2 -2.434,1.8 -4.9,3.017 -7.4,3.649 l -2.85,0.601 c -0.267,0 -0.517,0.033 -0.75,0.1 -1,0.1 -2.034,0.15 -3.101,0.15 -0.1,0 -0.2,0 -0.3,0 -0.1,0 -0.2,0 -0.3,0 -0.101,0 -0.2,0 -0.3,0 -0.134,-0.067 -0.284,-0.101 -0.45,-0.101 h -0.25 c -5.009,-0.33 -9.343,-1.98 -13,-4.95 -0.979,-0.805 -1.912,-1.705 -2.799,-2.7 z m 100.6,-53.175 c 1.706,1.48 3.372,3.038 5,4.675 0.208,0.212 0.416,0.429 0.625,0.65 -1.864,-1.801 -3.772,-3.501 -5.725,-5.1 0.03,-0.076 0.063,-0.151 0.1,-0.225 z" + clip-rule="evenodd" /> + <path + style="fill:#a3a3a3;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path24" + d="m 462.475,367.475 -51.85,51.825 -33.95,33.925 c -4.175,0.269 -8.983,0.427 -14.425,0.476 -0.167,0 -0.3,0 -0.4,0 -12.399,-1.934 -23.616,-6.184 -33.649,-12.75 -3.066,-1.967 -5.983,-4.134 -8.75,-6.5 -2.308,-1.941 -4.549,-4.024 -6.725,-6.25 -1.093,-1.142 -2.168,-2.316 -3.225,-3.525 -0.087,-0.092 -0.17,-0.184 -0.25,-0.274 -2.606,-3.002 -4.981,-6.077 -7.125,-9.226 -11.001,-16.266 -15.776,-34.574 -14.325,-54.925 0.565,-7.862 1.982,-15.279 4.25,-22.25 0.533,-1.658 1.116,-3.292 1.75,-4.9 0.9,-2.166 1.867,-4.316 2.9,-6.449 1.367,-2.634 2.85,-5.167 4.45,-7.601 0.6,-1 1.25,-2 1.95,-3 3.9,-5.566 8.566,-10.716 14,-15.45 0.167,-0.167 0.317,-0.3 0.45,-0.4 0.033,-0.1 0.101,-0.184 0.2,-0.25 3.947,-3.387 8.03,-6.37 12.25,-8.95 15.354,-9.381 32.52,-13.431 51.5,-12.15 h 0.05 c 0.601,0.033 3.55,0.4 8.851,1.1 0.1,0 0.25,0.017 0.449,0.05 1.534,0.267 3.017,0.567 4.45,0.9 0.2,0.033 0.417,0.083 0.65,0.15 9.01,2.083 17.368,5.499 25.075,10.25 -3.247,6.607 -7.688,12.724 -13.325,18.35 -9.269,9.287 -19.877,15.336 -31.825,18.15 -1.519,0.363 -3.06,0.671 -4.625,0.925 -0.456,-1.834 -1.14,-3.592 -2.05,-5.274 -0.101,-0.167 -0.2,-0.351 -0.3,-0.55 -1,-2.301 -2.517,-4.417 -4.551,-6.351 -0.182,-0.168 -0.365,-0.335 -0.55,-0.5 -0.334,-0.288 -0.676,-0.563 -1.024,-0.825 -0.246,-0.19 -0.496,-0.374 -0.75,-0.55 -3.474,-2.444 -7.615,-3.87 -12.426,-4.275 -2.301,-0.139 -4.501,0.011 -6.6,0.45 -3.92,0.822 -7.487,2.656 -10.7,5.5 -0.233,0.167 -0.45,0.351 -0.649,0.551 -1.766,1.505 -3.249,3.154 -4.45,4.949 -0.377,0.57 -0.728,1.154 -1.05,1.75 l -0.051,0.125 c -0.578,1.09 -1.062,2.231 -1.449,3.426 -0.261,0.81 -0.478,1.643 -0.65,2.5 -0.019,0.091 -0.035,0.183 -0.05,0.274 -0.053,0.273 -0.104,0.548 -0.15,0.825 -0.114,0.75 -0.198,1.517 -0.25,2.3 0,0.033 0,0.084 0,0.15 -0.09,1.646 -0.04,3.246 0.15,4.8 0.082,0.744 0.199,1.478 0.35,2.2 0.834,3.666 2.601,7.066 5.3,10.2 0.018,0.016 0.034,0.032 0.051,0.05 h 0.1 c 0.133,0.155 0.266,0.306 0.4,0.45 0.887,0.994 1.819,1.895 2.8,2.699 3.657,2.97 7.991,4.62 13,4.95 h 0.25 c 0.166,0 0.316,0.033 0.45,0.101 0.1,0 0.199,0 0.3,0 0.1,0 0.2,0 0.3,0 0.1,0 0.2,0 0.3,0 1.066,0 2.101,-0.051 3.101,-0.15 0.233,-0.066 0.483,-0.1 0.75,-0.1 L 354,357.8 c 2.5,-0.633 4.967,-1.85 7.4,-3.649 0.1,-0.101 0.199,-0.167 0.3,-0.2 0.434,-0.367 0.866,-0.733 1.3,-1.101 0.033,-0.066 0.1,-0.116 0.2,-0.149 0,-0.066 0.05,-0.117 0.149,-0.15 0.101,-0.133 0.184,-0.217 0.25,-0.25 3.982,-3.478 6.524,-7.652 7.625,-12.524 2.564,-0.304 5.081,-0.729 7.551,-1.275 0.097,-0.021 0.196,-0.046 0.3,-0.075 13.821,-3.098 26.063,-9.989 36.725,-20.675 6.097,-6.083 10.964,-12.683 14.601,-19.8 1.952,1.598 3.86,3.298 5.725,5.1 0.461,0.441 0.919,0.892 1.375,1.35 1.5,1.533 2.983,3.15 4.45,4.85 1.399,1.667 2.767,3.35 4.1,5.05 7.5,9.934 12.584,20.617 15.25,32.05 0.566,2.601 1.05,5.217 1.45,7.851 0.033,0.467 0.033,0.883 0,1.25 0.284,3.789 0.192,7.798 -0.276,12.022 z" + clip-rule="evenodd" /> + <path + style="fill:#8f8f8f;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path26" + d="m 474.175,453.9 h -0.024 v 0.05 c -0.367,0.066 -0.75,0.033 -1.15,-0.101 -0.192,-0.096 -0.359,-0.229 -0.5,-0.399 -0.115,-0.131 -0.216,-0.281 -0.3,-0.45 v -0.15 c -0.134,-0.333 -0.134,-0.683 0,-1.05 l -0.05,0.101 6.949,-25.551 c 0.101,-0.399 0.051,-0.816 -0.149,-1.25 -0.167,-0.366 -0.417,-0.683 -0.75,-0.949 -0.134,-0.034 -8.167,-8.784 -24.101,-26.25 1.967,-3.834 4.051,-10.117 6.25,-18.851 0.32,-1.295 0.612,-2.57 0.875,-3.825 -0.005,1.095 -0.005,2.328 0,3.7 0.032,6.125 -1.677,13.117 -5.125,20.976 15.934,17.466 23.967,26.216 24.101,26.25 0.333,0.267 0.583,0.583 0.75,0.949 0.2,0.434 0.25,0.851 0.149,1.25 l -5.975,21.976 -0.975,3.575 0.05,-0.101 c -0.012,0.032 -0.02,0.065 -0.025,0.1 z m 53.775,19.45 c 6.564,5.904 9.681,14.588 9.35,26.051 -0.467,13.733 -1.383,23.517 -2.75,29.35 -0.233,1.167 -0.767,2.25 -1.6,3.25 l -0.15,0.3 c -0.333,0.233 -0.649,0.45 -0.95,0.65 -0.8,0.533 -1.666,0.866 -2.6,1 -0.8,0.2 -1.684,0.399 -2.65,0.6 -5.933,1 -14.783,1.7 -26.55,2.101 -12.402,0.389 -21.669,-2.827 -27.8,-9.65 6.079,5.449 14.679,7.999 25.8,7.65 11.767,-0.4 20.617,-1.101 26.55,-2.101 0.967,-0.2 1.851,-0.399 2.65,-0.6 0.934,-0.134 1.8,-0.467 2.6,-1 0.301,-0.2 0.617,-0.417 0.95,-0.65 l 0.15,-0.3 c 0.833,-1 1.366,-2.083 1.6,-3.25 1.367,-5.833 2.283,-15.616 2.75,-29.35 0.296,-10.23 -2.154,-18.247 -7.35,-24.051 z m -31.125,3.225 c -0.357,0.039 -0.698,0.014 -1.025,-0.075 -0.333,-0.2 -0.6,-0.467 -0.8,-0.8 -0.066,-0.066 -0.1,-0.15 -0.1,-0.25 -0.134,-0.334 -0.15,-0.667 -0.051,-1 l 0.051,-0.05 6.75,-25.051 c 0.1,-0.433 0.083,-0.85 -0.051,-1.25 1.423,1.927 2.105,3.01 2.051,3.25 l -6.75,25.051 -0.051,0.05 c -0.011,0.039 -0.019,0.081 -0.024,0.125 z M 398.6,452.75 c -0.2,0.036 -0.399,0.069 -0.6,0.1 v -0.699 l 0.6,0.599 z" + clip-rule="evenodd" /> + <path + style="fill:#dbdbdb;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path28" + d="m 410.625,419.3 51.85,-51.825 c -0.286,2.505 -0.703,5.089 -1.25,7.75 -0.263,1.255 -0.555,2.53 -0.875,3.825 -2.199,8.733 -4.283,15.017 -6.25,18.851 15.934,17.466 23.967,26.216 24.101,26.25 0.333,0.267 0.583,0.583 0.75,0.949 0.2,0.434 0.25,0.851 0.149,1.25 l -6.95,25.55 0.05,-0.101 c -0.134,0.367 -0.134,0.717 0,1.05 V 453 c 0.084,0.169 0.185,0.319 0.3,0.45 0.141,0.17 0.308,0.304 0.5,0.399 0.4,0.134 0.783,0.167 1.15,0.101 v -0.05 h 0.024 l 25.325,-6.65 c 0.434,-0.233 0.833,-0.217 1.2,0.05 0.333,0.167 0.633,0.434 0.899,0.8 0.134,0.4 0.15,0.817 0.051,1.25 l -6.749,25.05 -0.051,0.05 c -0.1,0.333 -0.083,0.666 0.051,1 0,0.1 0.033,0.184 0.1,0.25 0.2,0.333 0.467,0.6 0.8,0.8 0.327,0.089 0.668,0.114 1.025,0.075 0.039,-0.01 0.081,-0.018 0.125,-0.025 v 0.101 l 0.1,-0.101 26.65,-7 c 1.576,1.142 2.992,2.408 4.25,3.8 5.195,5.805 7.646,13.821 7.35,24.051 -0.467,13.733 -1.383,23.517 -2.75,29.35 -0.233,1.167 -0.767,2.25 -1.6,3.25 l -0.15,0.3 c -0.333,0.233 -0.649,0.45 -0.95,0.65 -0.8,0.533 -1.666,0.866 -2.6,1 -0.8,0.2 -1.684,0.399 -2.65,0.6 -21.5,-21.6 -32.816,-32.934 -33.949,-34 L 430.55,438.2 c -0.2,-0.101 -0.366,-0.233 -0.5,-0.4 l -5.2,-5.25 -0.149,0.15 c -0.101,0.066 -0.2,0.166 -0.3,0.3 -3.216,-3.303 -7.273,-7.353 -12.176,-12.15 -0.525,-0.505 -1.058,-1.022 -1.6,-1.55 z m 10.725,16.1 0.101,0.05 h -0.101 v -0.05 z" + clip-rule="evenodd" /> + <path + style="fill:#c8c8c8;fill-rule:evenodd" + inkscape:connector-curvature="0" + id="path30" + d="m 421.35,435.4 v 0.05 h 0.101 c 0.833,-0.7 1.7,-1.417 2.6,-2.15 0.066,-0.133 0.184,-0.233 0.351,-0.3 0.1,-0.134 0.199,-0.233 0.3,-0.3 l 0.149,-0.15 5.2,5.25 c 0.134,0.167 0.3,0.3 0.5,0.4 l 60.101,60.35 c 1.133,1.066 12.449,12.4 33.949,34 -9.066,-2.467 -16.033,-6.467 -20.899,-12 -4.934,-5.633 -8.283,-9.116 -10.05,-10.45 -1.801,-1.3 -2.817,-1.866 -3.051,-1.699 -0.199,0.267 -0.449,0.483 -0.75,0.649 -1.199,0.9 -2.616,1.384 -4.25,1.45 -1.933,0 -3.6,-0.684 -5,-2.05 l -1.1,-1.101 c -0.233,-0.233 -0.417,-0.416 -0.55,-0.55 l -65.601,-65.75 c 0.067,-0.066 0.15,-0.116 0.25,-0.149 1.134,-0.733 2.267,-1.467 3.4,-2.2 0.233,-0.134 0.483,-0.3 0.75,-0.5 0.134,-0.101 0.3,-0.217 0.5,-0.351 l 3.1,-2.449 z" + clip-rule="evenodd" /> +</g></g> +</svg>
\ No newline at end of file diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index 8b04af766..0f9446713 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -13,15 +13,15 @@ dependencies { // JCenter etc. compile 'com.eftimoff:android-patternview:1.0.1@aar' - compile 'com.journeyapps:zxing-android-embedded:2.1.0@aar' - compile 'com.journeyapps:zxing-android-integration:2.1.0@aar' + compile 'com.journeyapps:zxing-android-embedded:2.3.0@aar' + compile 'com.journeyapps:zxing-android-integration:2.3.0@aar' compile 'com.google.zxing:core:3.2.0' compile 'com.jpardogo.materialtabstrip:library:1.0.9' compile 'it.neokree:MaterialNavigationDrawer:1.3.2' compile 'com.getbase:floatingactionbutton:1.9.0' compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0' compile "com.splitwise:tokenautocomplete:1.3.3@aar" - compile 'se.emilsjolander:stickylistheaders:2.5.2' + compile 'se.emilsjolander:stickylistheaders:2.6.0' compile 'org.sufficientlysecure:html-textview:1.1' // libs as submodules @@ -46,15 +46,15 @@ dependencyVerification { 'com.android.support:recyclerview-v7:859ed80e3761f8fc3126901260b208505120b5678bcf36ad2cfe9c453958b9c7', 'com.android.support:cardview-v7:4c03f2acce9925aa4f8845cb8cb37b3772c712b2438ff15f76c9e3d3bc63ead7', 'com.eftimoff:android-patternview:cec80e7265b8d8278b3c55b5fcdf551e4600ac2c8bf60d8dd76adca538af0b1e', - 'com.journeyapps:zxing-android-embedded:57fdf8a262135976201fd89f1bd8016ed16510be92e7ea721b999daeeeab8f7e', - 'com.journeyapps:zxing-android-integration:12caeb2608f11b6df77d27edc505ac8580abfc97a09a814b638cb9df0ba06906', + 'com.journeyapps:zxing-android-embedded:702a4f58154dbd9baa80f66b6a15410f7a4d403f3e73b66537a8bfb156b4b718', + 'com.journeyapps:zxing-android-integration:562737821b6d34c899b6fd2234ce0a8a31e02ff1fd7c59f6211961ce9767c7c8', 'com.google.zxing:core:7fe5a8ff437635a540e56317649937b768b454795ce999ed5f244f83373dee7b', 'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa', 'it.neokree:MaterialNavigationDrawer:a1221a410c5f71bf078c5c4768fdf06b402d6006c74f8e7b61199e4edc2aea57', 'com.getbase:floatingactionbutton:052aa2a94e49e5dccc97cb99f2add87e8698b84859f0e3ac181100c0bc7640ca', 'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13', 'com.splitwise:tokenautocomplete:20bee71cc59b3828eb000b684d46ddf738efd56b8fee453a509cd16fda42c8cb', - 'se.emilsjolander:stickylistheaders:bc158f51be842a5f92c6e2adc5782f6329b69796d76ebb8f39c77f611cdef573', + 'se.emilsjolander:stickylistheaders:8c05981ec5725be33f7cee5e68c13f3db49cd5c75f1aaeb04024920b1ef96ad4', 'org.sufficientlysecure:html-textview:ca24b1522be88378634093815ce9ff1b4920c72e7513a045a7846e14069ef988', // 'OpenKeychain.extern:openpgp-api-lib:f05a9215cdad3a6597e4c5ece6fcec92b178d218195a3e88d2c0937c48dd9580', // 'OpenKeychain.extern:openkeychain-api-lib:50f6ebb5452d3fdc7be137ccf857a0ff44d55539fcb7b91baef495766ed7f429', diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index d0f50d5cc..8c66176fd 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -624,6 +624,23 @@ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> </intent-filter> + <!-- VIEW from keyserver urls opened in a browser --> + <intent-filter android:label="@string/intent_import_key"> + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + + <data android:scheme="https"/> + <data android:scheme="http"/> + <!-- if we don't specify a host, pathPattern will be ignored--> + <data android:host="*"/> + <!-- convention for keyserver paths specified by internet draft + draft-shaw-openpgp-hkp-00.txt + (http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-3) --> + <data android:pathPattern="/pks/lookup.*"/> + </intent-filter> + <!-- Keychain's own Actions --> <!-- IMPORT_KEY with files TODO: does this work? --> <intent-filter android:label="@string/intent_import_key"> diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 480319081..cb8a53e25 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -242,7 +242,7 @@ public class HkpKeyserver extends Keyserver { String encodedQuery; try { - encodedQuery = URLEncoder.encode(query, "utf8"); + encodedQuery = URLEncoder.encode(query, "UTF8"); } catch (UnsupportedEncodingException e) { return null; } @@ -286,7 +286,7 @@ public class HkpKeyserver extends Keyserver { entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null)); // group 1 contains the full fingerprint (v4) or the long key id if available - // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr + // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.ENGLISH); if (fingerprintOrKeyId.length() > 16) { entry.setFingerprintHex(fingerprintOrKeyId); @@ -312,14 +312,13 @@ public class HkpKeyserver extends Keyserver { String tmp = uidMatcher.group(1).trim(); if (tmp.contains("%")) { if (tmp.contains("%%")) { - // This is a fix for issue #683 // The server encodes a percent sign as %%, so it is swapped out with its // urlencoded counterpart to prevent errors tmp = tmp.replace("%%", "%25"); } try { // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität". - tmp = (URLDecoder.decode(tmp, "UTF8")); + tmp = URLDecoder.decode(tmp, "UTF8"); } catch (UnsupportedEncodingException ignored) { // will never happen, because "UTF8" is supported } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 71843cd7f..c51edf59c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -167,7 +167,7 @@ public class OpenPgpService extends RemoteService { switch (requiredInput.mType) { case NFC_DECRYPT: case NFC_SIGN: { - // build PendingIntent for Yubikey NFC operations + // build PendingIntent for YubiKey NFC operations Intent intent = new Intent(context, NfcOperationActivity.class); // pass params through to activity that it can be returned again later to repeat pgp operation intent.putExtra(NfcOperationActivity.EXTRA_SERVICE_INTENT, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 03c250035..78137170d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -245,11 +245,11 @@ public class PassphraseCacheService extends Service { switch (keyType) { case DIVERT_TO_CARD: - if (Preferences.getPreferences(this).useDefaultYubikeyPin()) { - Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456"); - return new Passphrase("123456"); // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ + if (Preferences.getPreferences(this).useDefaultYubiKeyPin()) { + Log.d(Constants.TAG, "PassphraseCacheService: Using default YubiKey PIN: 123456"); + return new Passphrase("123456"); // default YubiKey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ } else { - Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN"); + Log.d(Constants.TAG, "PassphraseCacheService: NOT using default YubiKey PIN"); break; } case PASSPHRASE_EMPTY: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 20a280a54..59623a610 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -56,6 +56,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java index 1cd0aaf2f..db62d53c5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java @@ -123,7 +123,8 @@ public class CreateKeyYubiImportFragment extends Fragment implements NfcListener }); } - mListFragment = ImportKeysListFragment.newInstance(null, null, "0x" + mNfcFingerprint, true); + mListFragment = ImportKeysListFragment.newInstance(null, null, + "0x" + mNfcFingerprint, true, null); view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() { @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index f320a6d84..33209be86 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; @@ -85,25 +86,6 @@ public abstract class DecryptFragment extends CryptoOperationFragment { startActivity(viewKeyIntent); } -// protected void startPassphraseDialog(long subkeyId) { -// Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); -// intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId); -// startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); -// } -// -// protected void startNfcDecrypt(long subKeyId, Passphrase pin, byte[] encryptedSessionKey) { -// // build PendingIntent for Yubikey NFC operations -// Intent intent = new Intent(getActivity(), NfcActivity.class); -// intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); -// intent.putExtra(NfcActivity.EXTRA_SERVICE_INTENT, new Intent()); // not used, only relevant to OpenPgpService -// intent.putExtra(NfcActivity.EXTRA_KEY_ID, subKeyId); -// intent.putExtra(NfcActivity.EXTRA_PIN, pin); -// -// intent.putExtra(NfcActivity.EXTRA_NFC_ENC_SESSION_KEY, encryptedSessionKey); -// -// startActivityForResult(intent, REQUEST_CODE_NFC_DECRYPT); -// } - /** * * @return returns false if signature is invalid, key is revoked or expired. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index bf17c2991..390efddce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -17,8 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import java.util.Date; - import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; @@ -53,7 +51,6 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; @@ -62,6 +59,7 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.*; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index ddced7cce..458810541 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; @@ -201,7 +202,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } catch (IOException e) { Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), - Notify.Style.ERROR).show(); + Notify.Style.ERROR).show(this); return; } mSelectedFiles.requestFocus(); @@ -229,7 +230,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private void encryptClicked(boolean share) { if (mFilesModels.isEmpty()) { Notify.create(getActivity(), R.string.error_no_file_selected, - Notify.Style.ERROR).show(); + Notify.Style.ERROR).show(this); return; } if (share) { @@ -246,7 +247,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } else { if (mFilesModels.size() > 1) { Notify.create(getActivity(), R.string.error_multi_not_supported, - Notify.Style.ERROR).show(); + Notify.Style.ERROR).show(this); return; } showOutputFileDialog(); @@ -329,7 +330,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mFilesModels.isEmpty()) { Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) - .show(); + .show(this); return false; } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) { Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); @@ -346,12 +347,12 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mPassphrase == null) { Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(); + .show(this); return false; } if (mPassphrase.isEmpty()) { Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(); + .show(this); return false; } @@ -364,7 +365,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { // Files must be encrypted, only text can be signed-only right now if (!gotEncryptionKeys) { Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR) - .show(); + .show(this); return false; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 47645099d..3f9147cc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -44,6 +44,7 @@ import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; @@ -287,9 +288,9 @@ public class EncryptTextFragment extends CryptoOperationFragment { } protected boolean inputIsValid() { - if (mMessage == null) { - Notify.create(getActivity(), R.string.error_message, Notify.Style.ERROR) - .show(); + if (mMessage == null || mMessage.isEmpty()) { + Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR) + .show(this); return false; } @@ -298,12 +299,12 @@ public class EncryptTextFragment extends CryptoOperationFragment { if (mSymmetricPassphrase == null) { Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(); + .show(this); return false; } if (mSymmetricPassphrase.isEmpty()) { Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(); + .show(this); return false; } @@ -315,7 +316,7 @@ public class EncryptTextFragment extends CryptoOperationFragment { if (!gotEncryptionKeys && mSigningKeyId == 0) { Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) - .show(); + .show(this); return false; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 7fe5be793..5d9950db6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.service.CloudImportService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; +import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; @@ -62,6 +63,8 @@ public class ImportKeysActivity extends BaseNfcActivity { // Actions for internal use only: public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_FILE"; + public static final String ACTION_SEARCH_KEYSERVER_FROM_URL = Constants.INTENT_PREFIX + + "SEARCH_KEYSERVER_FROM_URL"; public static final String EXTRA_RESULT = "result"; // only used by ACTION_IMPORT_KEY @@ -112,15 +115,19 @@ public class ImportKeysActivity extends BaseNfcActivity { } if (action == null) { - startCloudFragment(savedInstanceState, null, false); - startListFragment(savedInstanceState, null, null, null); + startCloudFragment(savedInstanceState, null, false, null); + startListFragment(savedInstanceState, null, null, null, null); return; } if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) - // delegate action to ACTION_IMPORT_KEY - action = ACTION_IMPORT_KEY; + if (scheme.equals("http") || scheme.equals("https")) { + action = ACTION_SEARCH_KEYSERVER_FROM_URL; + } else { + // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) + // delegate action to ACTION_IMPORT_KEY + action = ACTION_IMPORT_KEY; + } } switch (action) { @@ -130,12 +137,12 @@ public class ImportKeysActivity extends BaseNfcActivity { if (dataUri != null) { // action: directly load data - startListFragment(savedInstanceState, null, dataUri, null); + startListFragment(savedInstanceState, null, dataUri, null, null); } else if (extras.containsKey(EXTRA_KEY_BYTES)) { byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES); // action: directly load data - startListFragment(savedInstanceState, importData, null, null); + startListFragment(savedInstanceState, importData, null, null, null); } break; } @@ -162,10 +169,10 @@ public class ImportKeysActivity extends BaseNfcActivity { if (query != null && query.length() > 0) { // display keyserver fragment with query - startCloudFragment(savedInstanceState, query, false); + startCloudFragment(savedInstanceState, query, false, null); // action: search immediately - startListFragment(savedInstanceState, null, null, query); + startListFragment(savedInstanceState, null, null, query, null); } else { Log.e(Constants.TAG, "Query is empty!"); return; @@ -181,10 +188,10 @@ public class ImportKeysActivity extends BaseNfcActivity { String query = "0x" + fingerprint; // display keyserver fragment with query - startCloudFragment(savedInstanceState, query, true); + startCloudFragment(savedInstanceState, query, true, null); // action: search immediately - startListFragment(savedInstanceState, null, null, query); + startListFragment(savedInstanceState, null, null, query, null); } } else { Log.e(Constants.TAG, @@ -200,7 +207,29 @@ public class ImportKeysActivity extends BaseNfcActivity { startFileFragment(savedInstanceState); // no immediate actions! - startListFragment(savedInstanceState, null, null, null); + startListFragment(savedInstanceState, null, null, null, null); + break; + } + case ACTION_SEARCH_KEYSERVER_FROM_URL: { + // need to process URL to get search query and keyserver authority + String query = dataUri.getQueryParameter("search"); + String keyserver = dataUri.getAuthority(); + // if query not specified, we still allow users to search the keyserver in the link + if (query == null) { + Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE, + Notify.Style.WARN).show(mTopFragment); + // we just set the keyserver + startCloudFragment(savedInstanceState, null, false, keyserver); + // it's not necessary to set the keyserver for ImportKeysListFragment since + // it'll be taken care of by ImportKeysCloudFragment when the user clicks + // the search button + startListFragment(savedInstanceState, null, null, null, null); + } else { + // we allow our users to edit the query if they wish + startCloudFragment(savedInstanceState, query, false, keyserver); + // search immediately + startListFragment(savedInstanceState, null, null, query, keyserver); + } break; } case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: { @@ -208,18 +237,31 @@ public class ImportKeysActivity extends BaseNfcActivity { startFileFragment(savedInstanceState); // no immediate actions! - startListFragment(savedInstanceState, null, null, null); + startListFragment(savedInstanceState, null, null, null, null); break; } default: { - startCloudFragment(savedInstanceState, null, false); - startListFragment(savedInstanceState, null, null, null); + startCloudFragment(savedInstanceState, null, false, null); + startListFragment(savedInstanceState, null, null, null, null); break; } } } - private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) { + + /** + * if the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately + * load content + * + * @param savedInstanceState + * @param bytes bytes containing list of keyrings to import + * @param dataUri uri to file to import keyrings from + * @param serverQuery query to search for on the keyserver + * @param keyserver keyserver authority to search on. If null will use keyserver from + * user preferences + */ + private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, + String serverQuery, String keyserver) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else // we could end up with overlapping fragments. @@ -227,8 +269,8 @@ public class ImportKeysActivity extends BaseNfcActivity { return; } - // Create an instance of the fragment - mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery); + mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false, + keyserver); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! @@ -259,7 +301,18 @@ public class ImportKeysActivity extends BaseNfcActivity { getSupportFragmentManager().executePendingTransactions(); } - private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit) { + /** + * loads the CloudFragment, which consists of the search bar, search button and settings icon + * visually. + * + * @param savedInstanceState + * @param query search query + * @param disableQueryEdit if true, user will not be able to edit the search query + * @param keyserver keyserver authority to use for search, if null will use keyserver + * specified in user preferences + */ + + private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else // we could end up with overlapping fragments. @@ -268,7 +321,7 @@ public class ImportKeysActivity extends BaseNfcActivity { } // Create an instance of the fragment - mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit); + mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit, keyserver); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java index 91ca93c36..1cd5c24f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java @@ -45,6 +45,7 @@ import java.util.List; public class ImportKeysCloudFragment extends Fragment { public static final String ARG_QUERY = "query"; public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit"; + public static final String ARG_KEYSERVER = "keyserver"; private ImportKeysActivity mImportActivity; @@ -54,13 +55,20 @@ public class ImportKeysCloudFragment extends Fragment { /** * Creates new instance of this fragment + * + * @param query query to search for + * @param disableQueryEdit if true, user cannot edit query + * @param keyserver specified keyserver authority to use. If null, will use keyserver + * specified in user preferences */ - public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit) { + public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit, + String keyserver) { ImportKeysCloudFragment frag = new ImportKeysCloudFragment(); Bundle args = new Bundle(); args.putString(ARG_QUERY, query); args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit); + args.putString(ARG_KEYSERVER, keyserver); frag.setArguments(args); @@ -151,8 +159,17 @@ public class ImportKeysCloudFragment extends Fragment { } private void search(String query) { - Preferences prefs = Preferences.getPreferences(getActivity()); - mImportActivity.loadCallback(new ImportKeysListFragment.CloudLoaderState(query, prefs.getCloudSearchPrefs())); + Preferences.CloudSearchPrefs cloudSearchPrefs; + String explicitKeyserver = getArguments().getString(ARG_KEYSERVER); + // no explicit keyserver passed + if (explicitKeyserver == null) { + cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs(); + } else { + // assume we are also meant to search keybase.io + cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, explicitKeyserver); + } + mImportActivity.loadCallback( + new ImportKeysListFragment.CloudLoaderState(query, cloudSearchPrefs)); toggleKeyboard(false); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index b9fdbea5c..bf7e41045 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -58,6 +58,7 @@ public class ImportKeysListFragment extends ListFragment implements private static final String ARG_BYTES = "bytes"; public static final String ARG_SERVER_QUERY = "query"; public static final String ARG_NON_INTERACTIVE = "non_interactive"; + public static final String ARG_KEYSERVER_URL = "keyserver_url"; private Activity mActivity; private ImportKeysAdapter mAdapter; @@ -78,7 +79,8 @@ public class ImportKeysListFragment extends ListFragment implements return mAdapter.getData(); } - /** Returns an Iterator (with size) of the selected data items. + /** + * Returns an Iterator (with size) of the selected data items. * This iterator is sort of a tradeoff, it's slightly more complex than an * ArrayList would have been, but we save some memory by just returning * relevant elements on demand. @@ -121,12 +123,36 @@ public class ImportKeysListFragment extends ListFragment implements } - public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery) { - return newInstance(bytes, dataUri, serverQuery, false); + /** + * Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified + * by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order + * + * @param bytes byte data containing list of keyrings to be imported + * @param dataUri file from which keyrings are to be imported + * @param serverQuery query to search for on keyserver + * @param keyserver if not null, will perform search on specified keyserver. Else, uses + * keyserver specified in user preferences + * @return fragment with arguments set based on passed parameters + */ + public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery, + String keyserver) { + return newInstance(bytes, dataUri, serverQuery, false, keyserver); } - public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, - String serverQuery, boolean nonInteractive) { + /** + * Visually consists of a list of keyrings with checkboxes to specify which are to be imported + * Can immediately load keyrings specified by any of its parameters + * + * @param bytes byte data containing list of keyrings to be imported + * @param dataUri file from which keyrings are to be imported + * @param serverQuery query to search for on keyserver + * @param nonInteractive if true, users will not be able to check/uncheck items in the list + * @param keyserver if set, will perform search on specified keyserver. If null, falls back + * to keyserver specified in user preferences + * @return fragment with arguments set based on passed parameters + */ + public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery, + boolean nonInteractive, String keyserver) { ImportKeysListFragment frag = new ImportKeysListFragment(); Bundle args = new Bundle(); @@ -134,6 +160,7 @@ public class ImportKeysListFragment extends ListFragment implements args.putParcelable(ARG_DATA_URI, dataUri); args.putString(ARG_SERVER_QUERY, serverQuery); args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive); + args.putString(ARG_KEYSERVER_URL, keyserver); frag.setArguments(args); @@ -180,16 +207,23 @@ public class ImportKeysListFragment extends ListFragment implements setListAdapter(mAdapter); Bundle args = getArguments(); - Uri dataUri = args.containsKey(ARG_DATA_URI) ? args.<Uri>getParcelable(ARG_DATA_URI) : null; - byte[] bytes = args.containsKey(ARG_BYTES) ? args.getByteArray(ARG_BYTES) : null; - String query = args.containsKey(ARG_SERVER_QUERY) ? args.getString(ARG_SERVER_QUERY) : null; - mNonInteractive = args.containsKey(ARG_NON_INTERACTIVE) ? args.getBoolean(ARG_NON_INTERACTIVE) : false; + Uri dataUri = args.getParcelable(ARG_DATA_URI); + byte[] bytes = args.getByteArray(ARG_BYTES); + String query = args.getString(ARG_SERVER_QUERY); + String keyserver = args.getString(ARG_KEYSERVER_URL); + mNonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false); if (dataUri != null || bytes != null) { mLoaderState = new BytesLoaderState(bytes, dataUri); } else if (query != null) { - Preferences prefs = Preferences.getPreferences(getActivity()); - mLoaderState = new CloudLoaderState(query, prefs.getCloudSearchPrefs()); + Preferences.CloudSearchPrefs cloudSearchPrefs; + if (keyserver == null) { + cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs(); + } else { + cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, keyserver); + } + + mLoaderState = new CloudLoaderState(query, cloudSearchPrefs); } getListView().setOnTouchListener(new OnTouchListener() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index 21747f77b..29f2511a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.TargetApi; import android.app.ProgressDialog; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.net.Uri; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; @@ -57,13 +58,12 @@ import java.util.Locale; public class ImportKeysProxyActivity extends FragmentActivity { public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE; + // implies activity returns scanned fingerprint as extra and does not import public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT"; public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT"; public static final String EXTRA_FINGERPRINT = "fingerprint"; - boolean returnResult; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -81,31 +81,18 @@ public class ImportKeysProxyActivity extends FragmentActivity { if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { // Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned - returnResult = false; processScannedContent(dataUri); - } else if (ACTION_SCAN_IMPORT.equals(action)) { - returnResult = false; - IntentIntegrator integrator = new IntentIntegrator(this); - integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) - .setPrompt(getString(R.string.import_qr_code_text)) - .setResultDisplayDuration(0) - .initiateScan(); - } else if (ACTION_SCAN_WITH_RESULT.equals(action)) { - returnResult = true; + } else if (ACTION_SCAN_WITH_RESULT.equals(action) + || ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) { IntentIntegrator integrator = new IntentIntegrator(this); integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) .setPrompt(getString(R.string.import_qr_code_text)) - .setResultDisplayDuration(0) - .initiateScan(); - } else if (ACTION_QR_CODE_API.equals(action)) { - // scan using xzing's Barcode Scanner from outside OpenKeychain - - returnResult = false; - new IntentIntegrator(this).initiateScan(); + .setResultDisplayDuration(0); + integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + integrator.initiateScan(); } else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { // Check to see if the Activity started due to an Android Beam if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - returnResult = false; handleActionNdefDiscovered(getIntent()); } else { Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); @@ -149,69 +136,63 @@ public class ImportKeysProxyActivity extends FragmentActivity { } private void processScannedContent(Uri uri) { + String action = getIntent().getAction(); Log.d(Constants.TAG, "scanned: " + uri); - String fingerprint = null; - // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { - fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - } + String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - if (fingerprint == null) { + if (ACTION_SCAN_WITH_RESULT.equals(action)) { + Intent result = new Intent(); + result.putExtra(EXTRA_FINGERPRINT, fingerprint); + setResult(RESULT_OK, result); + finish(); + } else { + importKeys(fingerprint); + } + } else { SingletonResult result = new SingletonResult( SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE); Intent intent = new Intent(); intent.putExtra(SingletonResult.EXTRA_RESULT, result); returnResult(intent); - return; - } - - if (returnResult) { - Intent result = new Intent(); - result.putExtra(EXTRA_FINGERPRINT, fingerprint); - setResult(RESULT_OK, result); - finish(); - } else { - importKeys(fingerprint); } } public void returnResult(Intent data) { - if (returnResult) { - setResult(RESULT_OK, data); - finish(); - } else { + String action = getIntent().getAction(); + + if (ACTION_QR_CODE_API.equals(action)) { // display last log message but as Toast for calls from outside OpenKeychain OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); String str = getString(result.getLog().getLast().mType.getMsgId()); Toast.makeText(this, str, Toast.LENGTH_LONG).show(); finish(); + } else { + setResult(RESULT_OK, data); + finish(); } } public void importKeys(byte[] keyringData) { - ParcelableKeyRing keyEntry = new ParcelableKeyRing(keyringData); ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>(); selectedEntries.add(keyEntry); startImportService(selectedEntries); - } public void importKeys(String fingerprint) { - ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>(); selectedEntries.add(keyEntry); startImportService(selectedEntries); - } - private void startImportService (ArrayList<ParcelableKeyRing> keyRings) { + private void startImportService(ArrayList<ParcelableKeyRing> keyRings) { // Message is received after importing is done in KeychainIntentService ServiceProgressHandler serviceHandler = new ServiceProgressHandler( @@ -284,7 +265,6 @@ public class ImportKeysProxyActivity extends FragmentActivity { // start service with intent startService(intent); - } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 4733dce01..96ce101b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -681,17 +681,16 @@ public class KeyListFragment extends LoaderFragment } private void showMultiExportDialog(long[] masterKeyIds) { - mIdsForRepeatAskPassphrase = new ArrayList<Long>(); - for(long id: masterKeyIds) { + mIdsForRepeatAskPassphrase = new ArrayList<>(); + for (long id : masterKeyIds) { try { if (PassphraseCacheService.getCachedPassphrase( getActivity(), id, id) == null) { - mIdsForRepeatAskPassphrase.add(Long.valueOf(id)); + mIdsForRepeatAskPassphrase.add(id); } } catch (PassphraseCacheService.KeyNotFoundException e) { // This happens when the master key is stripped // and ignore this key. - continue; } } mIndex = 0; @@ -700,8 +699,8 @@ public class KeyListFragment extends LoaderFragment return; } long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; - for(int i=0; i<mIdsForRepeatAskPassphrase.size(); ++i) { - idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i).longValue(); + for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) { + idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i); } mExportHelper.showExportKeysDialog(idsForMultiExport, Constants.Path.APP_DIR_FILE, @@ -710,7 +709,7 @@ public class KeyListFragment extends LoaderFragment private void startPassphraseActivity() { Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++).longValue(); + long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++); intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, masterKeyId); startActivityForResult(intent, REQUEST_REPEAT_PASSPHRASE); } @@ -718,7 +717,7 @@ public class KeyListFragment extends LoaderFragment @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_REPEAT_PASSPHRASE) { - if(resultCode != Activity.RESULT_OK) { + if (resultCode != Activity.RESULT_OK) { return; } if (mIndex < mIdsForRepeatAskPassphrase.size()) { @@ -726,8 +725,8 @@ public class KeyListFragment extends LoaderFragment return; } long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; - for(int i=0; i<mIdsForRepeatAskPassphrase.size(); ++i) { - idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i).longValue(); + for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) { + idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i); } mExportHelper.showExportKeysDialog(idsForMultiExport, Constants.Path.APP_DIR_FILE, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java deleted file mode 100644 index a1affbc39..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java +++ /dev/null @@ -1,314 +0,0 @@ -/** - * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann - * - * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details. - */ - -package org.sufficientlysecure.keychain.ui; - -import android.annotation.TargetApi; -import android.app.PendingIntent; -import android.content.Intent; -import android.content.IntentFilter; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.Build; -import android.os.Bundle; -import android.view.WindowManager; -import android.widget.Toast; - -import org.spongycastle.util.encoders.Hex; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Iso7816TLV; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant - * NFC devices. - * - * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf - */ -@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) -public class NfcIntentActivity extends BaseActivity { - - // special extra for OpenPgpService - public static final String EXTRA_DATA = "data"; - - private Intent mServiceIntent; - - private static final int TIMEOUT = 100000; - - private NfcAdapter mNfcAdapter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.d(Constants.TAG, "NfcActivity.onCreate"); - - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - - Intent intent = getIntent(); - Bundle data = intent.getExtras(); - String action = intent.getAction(); - - Log.d(Constants.TAG, action); - Log.d(Constants.TAG, intent.getDataString()); - - // TODO check fingerprint - // mFingerprint = data.getByteArray(EXTRA_FINGERPRINT); - - if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { - Log.d(Constants.TAG, "Action not supported: " + action); - finish(); - } - - try { - Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - - // Connect to the detected tag, setting a couple of settings - IsoDep isoDep = IsoDep.get(detectedTag); - isoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation - isoDep.connect(); - - nfcGreet(isoDep); - // nfcPin(isoDep, "yoloswag"); - nfcGetFingerprint(isoDep); - - } catch (IOException e) { - Log.e(Constants.TAG, "IOException!", e); - finish(); - } - } - - @Override - protected void initLayout() { - setContentView(R.layout.nfc_activity); - } - - /** - * Called when the system is about to start resuming a previous activity, - * disables NFC Foreground Dispatch - */ - public void onPause() { - super.onPause(); - Log.d(Constants.TAG, "NfcActivity.onPause"); - - // disableNfcForegroundDispatch(); - } - - /** - * Called when the activity will start interacting with the user, - * enables NFC Foreground Dispatch - */ - public void onResume() { - super.onResume(); - Log.d(Constants.TAG, "NfcActivity.onResume"); - - // enableNfcForegroundDispatch(); - } - - /** Handle NFC communication and return a result. - * - * This method is called by onNewIntent above upon discovery of an NFC tag. - * It handles initialization and login to the application, subsequently - * calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then - * finishes the activity with an appropiate result. - * - * On general communication, see also - * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx - * - * References to pages are generally related to the OpenPGP Application - * on ISO SmartCard Systems specification. - * - */ - private void nfcGreet(IsoDep isoDep) throws IOException { - - // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time. - // See specification, page 51 - String accepted = "9000"; - - // Command APDU (page 51) for SELECT FILE command (page 29) - String opening = - "00" // CLA - + "A4" // INS - + "04" // P1 - + "00" // P2 - + "06" // Lc (number of bytes) - + "D27600012401" // Data (6 bytes) - + "00"; // Le - if (!card(isoDep, opening).equals(accepted)) { // activate connection - toast("Opening Error!"); - setResult(RESULT_CANCELED, mServiceIntent); - finish(); - } - } - - private void nfcPin(IsoDep isoDep, String pin) throws IOException { - - String data = "00CA006E00"; - String fingerprint = card(isoDep, data); - - // Command APDU for VERIFY command (page 32) - String login = - "00" // CLA - + "20" // INS - + "00" // P1 - + "82" // P2 (PW1) - + String.format("%02x", pin.length()) // Lc - + Hex.toHexString(pin.getBytes()); - if ( ! card(isoDep, login).equals("9000")) { // login - toast("Pin Error!"); - setResult(RESULT_CANCELED, mServiceIntent); - finish(); - } - - } - - /** - * Gets the user ID - * - * @return the user id as "name <email>" - * @throws java.io.IOException - */ - public static String getUserId(IsoDep isoDep) throws IOException { - String info = "00CA006500"; - String data = "00CA005E00"; - return getName(card(isoDep, info)) + " <" + (new String(Hex.decode(getDataField(card(isoDep, data))))) + ">"; - } - - /** - * Gets the long key ID - * - * @return the long key id (last 16 bytes of the fingerprint) - * @throws java.io.IOException - */ - public static long getKeyId(IsoDep isoDep) throws IOException { - String keyId = nfcGetFingerprint(isoDep).substring(24); - Log.d(Constants.TAG, "keyId: " + keyId); - return Long.parseLong(keyId, 16); - } - - /** - * Gets the fingerprint of the signature key - * - * @return the fingerprint - * @throws java.io.IOException - */ - public static String nfcGetFingerprint(IsoDep isoDep) throws IOException { - String data = "00CA006E00"; - byte[] buf = isoDep.transceive(Hex.decode(data)); - - Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); - Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint()); - - Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); - if (fptlv != null) { - ByteBuffer fpbuf = ByteBuffer.wrap(fptlv.mV); - byte[] fp = new byte[20]; - fpbuf.get(fp); - Log.d(Constants.TAG, "fingerprint 1: " + KeyFormattingUtils.convertFingerprintToHex(fp)); - fpbuf.get(fp); - Log.d(Constants.TAG, "fingerprint 2: " + KeyFormattingUtils.convertFingerprintToHex(fp)); - fpbuf.get(fp); - Log.d(Constants.TAG, "fingerprint 3: " + KeyFormattingUtils.convertFingerprintToHex(fp)); - } - - return "nope"; - } - - /** - * Prints a message to the screen - * - * @param text the text which should be contained within the toast - */ - private void toast(String text) { - Toast.makeText(this, text, Toast.LENGTH_LONG).show(); - } - - /** - * Receive new NFC Intents to this activity only by enabling foreground dispatch. - * This can only be done in onResume! - */ - public void enableNfcForegroundDispatch() { - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - Intent nfcI = new Intent(this, NfcIntentActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); - PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, 0); - IntentFilter[] writeTagFilters = new IntentFilter[]{ - new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) - }; - - // https://code.google.com/p/android/issues/detail?id=62918 - // maybe mNfcAdapter.enableReaderMode(); ? - try { - mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); - } catch (IllegalStateException e) { - Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e); - } - Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!"); - } - - /** - * Disable foreground dispatch in onPause! - */ - public void disableNfcForegroundDispatch() { - mNfcAdapter.disableForegroundDispatch(this); - Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); - } - - /** - * Gets the name of the user out of the raw card output regarding card holder related data - * - * @param name the raw card holder related data from the card - * @return the name given in this data - */ - public static String getName(String name) { - String slength; - int ilength; - name = name.substring(6); - slength = name.substring(0, 2); - ilength = Integer.parseInt(slength, 16) * 2; - name = name.substring(2, ilength + 2); - name = (new String(Hex.decode(name))).replace('<', ' '); - return (name); - } - - /** - * Reduces the raw data from the card by four characters - * - * @param output the raw data from the card - * @return the data field of that data - */ - private static String getDataField(String output) { - return output.substring(0, output.length() - 4); - } - - /** - * Communicates with the OpenPgpCard via the APDU - * - * @param hex the hexadecimal APDU - * @return The answer from the card - * @throws java.io.IOException throws an exception if something goes wrong - */ - public static String card(IsoDep isoDep, String hex) throws IOException { - return getHex(isoDep.transceive(Hex.decode(hex))); - } - - /** - * Converts a byte array into an hex string - * - * @param raw the byte array representation - * @return the hexadecimal string representation - */ - public static String getHex(byte[] raw) { - return new String(Hex.encode(raw)); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java index a0b38c2b2..aa66053fa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -6,9 +6,7 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.TargetApi; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.view.WindowManager; @@ -27,10 +25,9 @@ import java.io.IOException; /** * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant * NFC devices. - * + * <p/> * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf */ -@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) public class NfcOperationActivity extends BaseNfcActivity { public static final String EXTRA_REQUIRED_INPUT = "required_input"; @@ -57,7 +54,7 @@ public class NfcOperationActivity extends BaseNfcActivity { mServiceIntent = data.getParcelable(EXTRA_SERVICE_INTENT); // obtain passphrase for this subkey - obtainYubikeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput)); + obtainYubiKeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput)); } @Override @@ -71,16 +68,15 @@ public class NfcOperationActivity extends BaseNfcActivity { CryptoInputParcel inputParcel = new CryptoInputParcel(mRequiredInput.mSignatureTime); switch (mRequiredInput.mType) { - - case NFC_DECRYPT: + case NFC_DECRYPT: { for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) { byte[] hash = mRequiredInput.mInputHashes[i]; byte[] decryptedSessionKey = nfcDecryptSessionKey(hash); inputParcel.addCryptoData(hash, decryptedSessionKey); } break; - - case NFC_SIGN: + } + case NFC_SIGN: { for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) { byte[] hash = mRequiredInput.mInputHashes[i]; int algo = mRequiredInput.mSignAlgos[i]; @@ -88,6 +84,7 @@ public class NfcOperationActivity extends BaseNfcActivity { inputParcel.addCryptoData(hash, signedHash); } break; + } } if (mServiceIntent != null) { @@ -100,7 +97,6 @@ public class NfcOperationActivity extends BaseNfcActivity { } finish(); - } @Override @@ -108,7 +104,7 @@ public class NfcOperationActivity extends BaseNfcActivity { // avoid a loop Preferences prefs = Preferences.getPreferences(this); - if (prefs.useDefaultYubikeyPin()) { + if (prefs.useDefaultYubiKeyPin()) { toast(getString(R.string.error_pin_nodefault)); setResult(RESULT_CANCELED); finish(); @@ -119,7 +115,7 @@ public class NfcOperationActivity extends BaseNfcActivity { PassphraseCacheService.clearCachedPassphrase( this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId()); - obtainYubikeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput)); - + obtainYubiKeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput)); } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 84612002f..4e926c0fe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -314,7 +314,7 @@ public class PassphraseDialogActivity extends FragmentActivity { mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); mPassphraseEditText.setOnEditorActionListener(this); - if (keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubikeyPin()) { + if (keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) { mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD); } else if (keyType == CanonicalizedSecretKey.SecretKeyType.PIN) { mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 210960b65..442bdf8f7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -107,10 +107,10 @@ public class SettingsActivity extends PreferenceActivity { values[i] = "" + valueIds[i]; } - initializeUseDefaultYubikeyPin( + initializeUseDefaultYubiKeyPin( (CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN)); - initializeUseNumKeypadForYubikeyPin( + initializeUseNumKeypadForYubiKeyPin( (CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN)); } @@ -262,10 +262,10 @@ public class SettingsActivity extends PreferenceActivity { values[i] = "" + valueIds[i]; } - initializeUseDefaultYubikeyPin( + initializeUseDefaultYubiKeyPin( (CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN)); - initializeUseNumKeypadForYubikeyPin( + initializeUseNumKeypadForYubiKeyPin( (CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN)); } } @@ -335,23 +335,23 @@ public class SettingsActivity extends PreferenceActivity { return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences.getPreferredKeyserver(); } - private static void initializeUseDefaultYubikeyPin(final CheckBoxPreference mUseDefaultYubikeyPin) { - mUseDefaultYubikeyPin.setChecked(sPreferences.useDefaultYubikeyPin()); - mUseDefaultYubikeyPin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + private static void initializeUseDefaultYubiKeyPin(final CheckBoxPreference mUseDefaultYubiKeyPin) { + mUseDefaultYubiKeyPin.setChecked(sPreferences.useDefaultYubiKeyPin()); + mUseDefaultYubiKeyPin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { - mUseDefaultYubikeyPin.setChecked((Boolean) newValue); - sPreferences.setUseDefaultYubikeyPin((Boolean) newValue); + mUseDefaultYubiKeyPin.setChecked((Boolean) newValue); + sPreferences.setUseDefaultYubiKeyPin((Boolean) newValue); return false; } }); } - private static void initializeUseNumKeypadForYubikeyPin(final CheckBoxPreference mUseNumKeypadForYubikeyPin) { - mUseNumKeypadForYubikeyPin.setChecked(sPreferences.useNumKeypadForYubikeyPin()); - mUseNumKeypadForYubikeyPin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + private static void initializeUseNumKeypadForYubiKeyPin(final CheckBoxPreference mUseNumKeypadForYubiKeyPin) { + mUseNumKeypadForYubiKeyPin.setChecked(sPreferences.useNumKeypadForYubiKeyPin()); + mUseNumKeypadForYubiKeyPin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { - mUseNumKeypadForYubikeyPin.setChecked((Boolean) newValue); - sPreferences.setUseNumKeypadForYubikeyPin((Boolean) newValue); + mUseNumKeypadForYubiKeyPin.setChecked((Boolean) newValue); + sPreferences.setUseNumKeypadForYubiKeyPin((Boolean) newValue); return false; } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index 9f2e46b38..8f025c769 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -20,6 +20,9 @@ package org.sufficientlysecure.keychain.ui; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -28,6 +31,8 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.base.BaseActivity; +import org.sufficientlysecure.keychain.ui.dialog.AddKeyserverDialogFragment; +import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.widget.Editor; import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; @@ -95,7 +100,7 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi Intent intent = getIntent(); String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); makeServerList(servers); - } + } @Override protected void initLayout() { @@ -124,10 +129,63 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi } + // button to add keyserver clicked public void onClick(View v) { + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + Bundle data = message.getData(); + switch (message.what) { + case AddKeyserverDialogFragment.MESSAGE_OKAY: { + boolean verified = data.getBoolean(AddKeyserverDialogFragment.MESSAGE_VERIFIED); + if (verified) { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_verified, Notify.Style.OK).show(); + } else { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_without_verification, + Notify.Style.WARN).show(); + } + String keyserver = data.getString(AddKeyserverDialogFragment.MESSAGE_KEYSERVER); + addKeyserver(keyserver); + break; + } + case AddKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: { + AddKeyserverDialogFragment.FailureReason failureReason = + (AddKeyserverDialogFragment.FailureReason) data.getSerializable( + AddKeyserverDialogFragment.MESSAGE_FAILURE_REASON); + switch (failureReason) { + case CONNECTION_FAILED: { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_connection_failed, + Notify.Style.ERROR).show(); + break; + } + case INVALID_URL: { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_invalid_url, + Notify.Style.ERROR).show(); + break; + } + } + break; + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + AddKeyserverDialogFragment dialogFragment = AddKeyserverDialogFragment + .newInstance(messenger, R.string.add_keyserver_dialog_title); + dialogFragment.show(getSupportFragmentManager(), "addKeyserverDialog"); + } + + public void addKeyserver(String keyserverUrl) { KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false); view.setEditorListener(this); + view.setValue(keyserverUrl); mEditors.addView(view); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index b063df2fb..5466c0b9a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -281,7 +281,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID); - showYubikeyFragment(nfcFingerprints, nfcUserId, nfcAid); + showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); } } @@ -593,12 +593,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements } } - showYubikeyFragment(nfcFingerprints, nfcUserId, nfcAid); + showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); } - public void showYubikeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) { - ViewKeyYubikeyFragment frag = ViewKeyYubikeyFragment.newInstance( + public void showYubiKeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) { + ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance( nfcFingerprints, nfcUserId, nfcAid); FragmentManager manager = getSupportFragmentManager(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubikeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java index 1482b70a7..812874456 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubikeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java @@ -1,10 +1,26 @@ -package org.sufficientlysecure.keychain.ui; +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package org.sufficientlysecure.keychain.ui; import java.nio.ByteBuffer; import java.util.Arrays; -import android.app.ProgressDialog; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; @@ -29,11 +45,9 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; -import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; - -public class ViewKeyYubikeyFragment extends Fragment +public class ViewKeyYubiKeyFragment extends Fragment implements LoaderCallbacks<Cursor> { public static final String ARG_FINGERPRINT = "fingerprint"; @@ -46,9 +60,8 @@ public class ViewKeyYubikeyFragment extends Fragment private Button vButton; private TextView vStatus; - public static ViewKeyYubikeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) { - - ViewKeyYubikeyFragment frag = new ViewKeyYubikeyFragment(); + public static ViewKeyYubiKeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) { + ViewKeyYubiKeyFragment frag = new ViewKeyYubiKeyFragment(); Bundle args = new Bundle(); args.putByteArray(ARG_FINGERPRINT, fingerprints); @@ -57,7 +70,6 @@ public class ViewKeyYubikeyFragment extends Fragment frag.setArguments(args); return frag; - } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 5447c5f96..139512ba9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -82,12 +82,12 @@ public class ImportKeysListLoader @Override protected void onStartLoading() { - forceLoad(); + super.forceLoad(); } @Override protected void onStopLoading() { - cancelLoad(); + super.cancelLoad(); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index 4f6d5807e..1d09b281f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -1,5 +1,22 @@ -package org.sufficientlysecure.keychain.ui.base; +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package org.sufficientlysecure.keychain.ui.base; import java.io.IOException; import java.nio.ByteBuffer; @@ -35,12 +52,14 @@ import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; - public abstract class BaseNfcActivity extends BaseActivity { public static final int REQUEST_CODE_PASSPHRASE = 1; protected Passphrase mPin; + protected boolean mPw1ValidForMultipleSignatures; + protected boolean mPw1ValidatedForSignature; + protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming? private NfcAdapter mNfcAdapter; private IsoDep mIsoDep; @@ -108,10 +127,10 @@ public abstract class BaseNfcActivity extends BaseActivity { enableNfcForegroundDispatch(); } - protected void obtainYubikeyPin(RequiredInputParcel requiredInput) { + protected void obtainYubiKeyPin(RequiredInputParcel requiredInput) { Preferences prefs = Preferences.getPreferences(this); - if (prefs.useDefaultYubikeyPin()) { + if (prefs.useDefaultYubiKeyPin()) { mPin = new Passphrase("123456"); return; } @@ -123,7 +142,7 @@ public abstract class BaseNfcActivity extends BaseActivity { } - protected void setYubikeyPin(Passphrase pin) { + protected void setYubiKeyPin(Passphrase pin) { mPin = pin; } @@ -181,28 +200,14 @@ public abstract class BaseNfcActivity extends BaseActivity { + "06" // Lc (number of bytes) + "D27600012401" // Data (6 bytes) + "00"; // Le - if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection + if ( ! nfcCommunicate(opening).endsWith(accepted)) { // activate connection throw new IOException("Initialization failed!"); } - if (mPin != null) { - - byte[] pin = new String(mPin.getCharArray()).getBytes(); - - // Command APDU for VERIFY command (page 32) - String login = - "00" // CLA - + "20" // INS - + "00" // P1 - + "82" // P2 (PW1) - + String.format("%02x", pin.length) // Lc - + Hex.toHexString(pin); - if (!nfcCommunicate(login).equals(accepted)) { // login - handlePinError(); - return; - } - - } + byte[] pwStatusBytes = nfcGetPwStatusBytes(); + mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1); + mPw1ValidatedForSignature = false; + mPw1ValidatedForDecrypt = false; onNfcPerform(); @@ -217,7 +222,6 @@ public abstract class BaseNfcActivity extends BaseActivity { final String nfcUserId = nfcGetUserId(); final byte[] nfcAid = nfcGetAid(); - String fp = KeyFormattingUtils.convertFingerprintToHex(nfcFingerprints); final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); try { @@ -282,6 +286,15 @@ public abstract class BaseNfcActivity extends BaseActivity { return fptlv.mV; } + /** Return the PW Status Bytes from the card. This is a simple DO; no TLV decoding needed. + * + * @return Seven bytes in fixed format, plus 0x9000 status word at the end. + */ + public byte[] nfcGetPwStatusBytes() throws IOException { + String data = "00CA00C400"; + return mIsoDep.transceive(Hex.decode(data)); + } + /** Return the fingerprint from application specific data stored on tag, or * null if it doesn't exist. * @@ -320,6 +333,9 @@ public abstract class BaseNfcActivity extends BaseActivity { * @return a big integer representing the MPI for the given hash */ public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException { + if (!mPw1ValidatedForSignature) { + nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing) + } // dsi, including Lc String dsi; @@ -394,6 +410,10 @@ public abstract class BaseNfcActivity extends BaseActivity { Log.d(Constants.TAG, "final response:" + status); + if (!mPw1ValidForMultipleSignatures) { + mPw1ValidatedForSignature = false; + } + if ( ! "9000".equals(status)) { throw new IOException("Bad NFC response code: " + status); } @@ -413,6 +433,10 @@ public abstract class BaseNfcActivity extends BaseActivity { * @return the decoded session key */ public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException { + if (!mPw1ValidatedForDecrypt) { + nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption) + } + String firstApdu = "102a8086fe"; String secondApdu = "002a808603"; String le = "00"; @@ -436,6 +460,38 @@ public abstract class BaseNfcActivity extends BaseActivity { return Hex.decode(decryptedSessionKey); } + /** Verifies the user's PW1 with the appropriate mode. + * + * @param mode This is 0x81 for signing, 0x82 for everything else + */ + public void nfcVerifyPIN(int mode) throws IOException { + if (mPin != null) { + byte[] pin = new String(mPin.getCharArray()).getBytes(); + // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time. + // See specification, page 51 + String accepted = "9000"; + + // Command APDU for VERIFY command (page 32) + String login = + "00" // CLA + + "20" // INS + + "00" // P1 + + String.format("%02x", mode) // P2 + + String.format("%02x", pin.length) // Lc + + Hex.toHexString(pin); + if (!nfcCommunicate(login).equals(accepted)) { // login + handlePinError(); + throw new IOException("Bad PIN!"); + } + + if (mode == 0x81) { + mPw1ValidatedForSignature = true; + } else if (mode == 0x82) { + mPw1ValidatedForDecrypt = true; + } + } + } + /** * Prints a message to the screen * @@ -451,8 +507,9 @@ public abstract class BaseNfcActivity extends BaseActivity { */ public void enableNfcForegroundDispatch() { mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - if(mNfcAdapter == null) return; - + if (mNfcAdapter == null) { + return; + } Intent nfcI = new Intent(this, getClass()) .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT); @@ -474,9 +531,10 @@ public abstract class BaseNfcActivity extends BaseActivity { * Disable foreground dispatch in onPause! */ public void disableNfcForegroundDispatch() { - if(mNfcAdapter != null) { - mNfcAdapter.disableForegroundDispatch(this); + if (mNfcAdapter == null) { + return; } + mNfcAdapter.disableForegroundDispatch(this); Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java index b136492b4..232a39f86 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -package org.sufficientlysecure.keychain.ui; +package org.sufficientlysecure.keychain.ui.base; import android.app.Activity; import android.content.Intent; @@ -29,6 +29,8 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.ui.NfcOperationActivity; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; /** * All fragments executing crypto operations need to extend this class. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java new file mode 100644 index 000000000..cbef5950f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui.dialog; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import android.test.suitebuilder.TestSuiteBuilder; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.TlsHelper; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +public class AddKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_TITLE = "title"; + + public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_VERIFICATION_FAILED = 2; + + public static final String MESSAGE_KEYSERVER = "new_keyserver"; + public static final String MESSAGE_VERIFIED = "verified"; + public static final String MESSAGE_FAILURE_REASON = "failure_reason"; + + private Messenger mMessenger; + private EditText mKeyserverEditText; + private CheckBox mVerifyKeyserverCheckBox; + + public static enum FailureReason { + INVALID_URL, + CONNECTION_FAILED + } + + ; + + /** + * Creates new instance of this dialog fragment + * + * @param title title of dialog + * @param messenger to communicate back after setting the passphrase + * @return + */ + public static AddKeyserverDialogFragment newInstance(Messenger messenger, int title) { + AddKeyserverDialogFragment frag = new AddKeyserverDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_TITLE, title); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + int title = getArguments().getInt(ARG_TITLE); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); + + alert.setTitle(title); + + LayoutInflater inflater = activity.getLayoutInflater(); + View view = inflater.inflate(R.layout.add_keyserver_dialog, null); + alert.setView(view); + + mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text); + mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox); + + // we don't want dialog to be dismissed on click, thereby requiring the hack seen below + // and in onStart + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + // we need to have an empty listener to prevent errors on some devices as mentioned + // at http://stackoverflow.com/q/13746412/3000919 + // actual listener set in onStart + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + // Hack to open keyboard. + // This is the only method that I found to work across all Android versions + // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ + // Notes: * onCreateView can't be used because we want to add buttons to the dialog + // * opening in onActivityCreated does not work on Android 4.4 + mKeyserverEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + mKeyserverEditText.post(new Runnable() { + @Override + public void run() { + InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mKeyserverEditText, InputMethodManager.SHOW_IMPLICIT); + } + }); + } + }); + mKeyserverEditText.requestFocus(); + + mKeyserverEditText.setImeActionLabel(getString(android.R.string.ok), + EditorInfo.IME_ACTION_DONE); + mKeyserverEditText.setOnEditorActionListener(this); + + return alert.show(); + } + + @Override + public void onStart() { + super.onStart(); + AlertDialog addKeyserverDialog = (AlertDialog) getDialog(); + if (addKeyserverDialog != null) { + Button positiveButton = addKeyserverDialog.getButton(Dialog.BUTTON_POSITIVE); + positiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String keyserverUrl = mKeyserverEditText.getText().toString(); + if (mVerifyKeyserverCheckBox.isChecked()) { + verifyConnection(keyserverUrl); + } else { + dismiss(); + // return unverified keyserver back to activity + addKeyserver(keyserverUrl, false); + } + } + }); + } + } + + public void addKeyserver(String keyserver, boolean verified) { + dismiss(); + Bundle data = new Bundle(); + data.putString(MESSAGE_KEYSERVER, keyserver); + data.putBoolean(MESSAGE_VERIFIED, verified); + + sendMessageToHandler(MESSAGE_OKAY, data); + } + + public void verificationFailed(FailureReason reason) { + Bundle data = new Bundle(); + data.putSerializable(MESSAGE_FAILURE_REASON, reason); + + sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data); + } + + public void verifyConnection(String keyserver) { + + new AsyncTask<String, Void, FailureReason>() { + ProgressDialog mProgressDialog; + String mKeyserver; + + @Override + protected void onPreExecute() { + mProgressDialog = new ProgressDialog(getActivity()); + mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_url)); + mProgressDialog.setCancelable(false); + mProgressDialog.show(); + } + + @Override + protected FailureReason doInBackground(String... keyservers) { + mKeyserver = keyservers[0]; + FailureReason reason = null; + try { + // replace hkps/hkp scheme and reconstruct Uri + Uri keyserverUri = Uri.parse(mKeyserver); + String scheme = keyserverUri.getScheme(); + String schemeSpecificPart = keyserverUri.getSchemeSpecificPart(); + String fragment = keyserverUri.getFragment(); + if (scheme == null) throw new MalformedURLException(); + if (scheme.equalsIgnoreCase("hkps")) scheme = "https"; + else if (scheme.equalsIgnoreCase("hkp")) scheme = "http"; + URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment); + + Log.d("Converted URL", newKeyserver.toString()); + TlsHelper.openConnection(newKeyserver.toURL()).getInputStream(); + } catch (TlsHelper.TlsHelperException e) { + reason = FailureReason.CONNECTION_FAILED; + } catch (MalformedURLException e) { + Log.w(Constants.TAG, "Invalid keyserver URL entered by user."); + reason = FailureReason.INVALID_URL; + } catch (URISyntaxException e) { + Log.w(Constants.TAG, "Invalid keyserver URL entered by user."); + reason = FailureReason.INVALID_URL; + } catch (IOException e) { + Log.w(Constants.TAG, "Could not connect to entered keyserver url"); + reason = FailureReason.CONNECTION_FAILED; + } + return reason; + } + + @Override + protected void onPostExecute(FailureReason failureReason) { + mProgressDialog.dismiss(); + if (failureReason == null) { + addKeyserver(mKeyserver, true); + } else { + verificationFailed(failureReason); + } + } + }.execute(keyserver); + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + + // hide keyboard on dismiss + hideKeyboard(); + } + + private void hideKeyboard() { + if (getActivity() == null) { + return; + } + InputMethodManager inputManager = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + + //check if no view has focus: + View v = getActivity().getCurrentFocus(); + if (v == null) + return; + + inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); + } + + /** + * Associate the "done" button on the soft keyboard with the okay button in the view + */ + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + AlertDialog dialog = ((AlertDialog) getDialog()); + Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + + bt.performClick(); + return true; + } + return false; + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what Message integer you want to send + */ + private void sendMessageToHandler(Integer what, Bundle data) { + Message msg = Message.obtain(); + msg.what = what; + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 44c1e6b6c..303687315 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -109,21 +109,21 @@ public class Preferences { return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true); } - public boolean useDefaultYubikeyPin() { + public boolean useDefaultYubiKeyPin() { return mSharedPreferences.getBoolean(Pref.USE_DEFAULT_YUBIKEY_PIN, false); } - public void setUseDefaultYubikeyPin(boolean useDefaultYubikeyPin) { + public void setUseDefaultYubiKeyPin(boolean useDefaultYubikeyPin) { SharedPreferences.Editor editor = mSharedPreferences.edit(); editor.putBoolean(Pref.USE_DEFAULT_YUBIKEY_PIN, useDefaultYubikeyPin); editor.commit(); } - public boolean useNumKeypadForYubikeyPin() { + public boolean useNumKeypadForYubiKeyPin() { return mSharedPreferences.getBoolean(Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN, true); } - public void setUseNumKeypadForYubikeyPin(boolean useNumKeypadForYubikeyPin) { + public void setUseNumKeypadForYubiKeyPin(boolean useNumKeypadForYubikeyPin) { SharedPreferences.Editor editor = mSharedPreferences.edit(); editor.putBoolean(Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN, useNumKeypadForYubikeyPin); editor.commit(); @@ -193,6 +193,11 @@ public class Preferences { public final boolean searchKeybase; public final String keyserver; + /** + * @param searchKeyserver should passed keyserver be searched + * @param searchKeybase should keybase.io be searched + * @param keyserver the keyserver url authority to search on + */ public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) { this.searchKeyserver = searchKeyserver; this.searchKeybase = searchKeybase; diff --git a/OpenKeychain/src/main/res/layout/add_keyserver_dialog.xml b/OpenKeychain/src/main/res/layout/add_keyserver_dialog.xml new file mode 100644 index 000000000..78e9247ea --- /dev/null +++ b/OpenKeychain/src/main/res/layout/add_keyserver_dialog.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="16dp" + android:paddingLeft="24dp" + android:paddingRight="24dp" + android:paddingTop="16dp"> + + <EditText + android:id="@+id/keyserver_url_edit_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="8dp" + android:ems="10" + android:hint="@string/label_enter_keyserver_url" + android:imeOptions="actionDone" /> + + <CheckBox + android:id="@+id/verify_keyserver_checkbox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_verify_keyserver" + android:checked="true"/> + +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/encrypt_files_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_files_fragment.xml index b75ec5022..8fd2c79fc 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_files_fragment.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_files_fragment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> @@ -12,4 +12,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> -</LinearLayout>
\ No newline at end of file +</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml index 3c21291cd..7645918f4 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml @@ -1,25 +1,24 @@ <?xml version="1.0" encoding="utf-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" - android:fillViewport="true"> + android:layout_height="match_parent"> - <LinearLayout + <ScrollView android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="4dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:orientation="vertical"> + android:layout_height="match_parent" + android:fillViewport="true"> <EditText android:id="@+id/encrypt_text_text" android:layout_width="match_parent" - android:layout_height="0dip" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" android:gravity="top" android:inputType="text|textCapSentences|textMultiLine|textLongMessage" - android:hint="@string/encrypt_content_edit_text_hint" - android:layout_weight="1" /> + android:hint="@string/encrypt_content_edit_text_hint" /> - </LinearLayout> -</ScrollView> + </ScrollView> + +</RelativeLayout> diff --git a/OpenKeychain/src/main/res/raw/help_changelog.md b/OpenKeychain/src/main/res/raw/help_changelog.md index 18203b1c5..98639de71 100644 --- a/OpenKeychain/src/main/res/raw/help_changelog.md +++ b/OpenKeychain/src/main/res/raw/help_changelog.md @@ -2,8 +2,9 @@ ## 3.2beta2 + * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,... * Material design - * Integration of QR Scanner (New permissions required) + * Integration of QR Code Scanning (New permissions required) * Improved key creation wizard * Fix missing contacts after sync * Requires Android 4 @@ -13,6 +14,7 @@ * Fix: Some valid keys were shown revoked or expired * Don't accept signatures by expired or revoked subkeys * Keybase.io support in advanced view + * Method to update all keys at once ## 3.1.2 @@ -35,7 +37,7 @@ * Redesigned decrypt screen * New icon usage and colors * Fix import of secret keys from Symantec Encryption Desktop - * Subkey IDs on Yubikeys are now checked correctly + * Experimental YubiKey support: Subkey IDs are now checked correctly ## 3.0.1 @@ -46,7 +48,6 @@ ## 3.0 - * Full support for Yubikey signature generation and decryption! * Propose installable compatible apps in apps list * New design for decryption screens * Many fixes for key import, also fixes stripped keys @@ -55,12 +56,13 @@ * Fixing user id revocation certificates * New cloud search (searches over traditional keyservers and keybase.io) * Support for stripping keys inside OpenKeychain + * Experimental YubiKey support: Support for signature generation and decryption ## 2.9.2 * Fix keys broken in 2.9.1 - * Yubikey decryption now working via API + * Experimental YubiKey support: Decryption now working via API ## 2.9.1 @@ -69,7 +71,7 @@ * Fix key flags handling (now supporting Mailvelope 0.7 keys) * Improved passphrase handling * Key sharing via SafeSlinger - * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain + * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain * Fix usage of stripped keys * SHA256 as default for compatibility * Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API @@ -80,7 +82,7 @@ * Fixing crashes introduced in v2.8 * Experimental ECC support - * Experimental Yubikey support (signing-only with imported keys) + * Experimental YubiKey support: Only signing with imported keys ## 2.8 diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index c1ff5a1d1..33fc362a1 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -86,6 +86,7 @@ <string name="btn_encrypt_text">"Encrypt text"</string> <string name="btn_add_email">"Add additional email address"</string> <string name="btn_unlock">"Unlock"</string> + <string name="btn_add_keyserver">"Add"</string> <!-- menu --> <string name="menu_preferences">"Settings"</string> @@ -152,6 +153,8 @@ <string name="label_enable_compression">"Enable compression"</string> <string name="label_encrypt_filenames">"Encrypt filenames"</string> <string name="label_hidden_recipients">"Hide recipients"</string> + <string name="label_verify_keyserver">"Verify Keyserver"</string> + <string name="label_enter_keyserver_url">"Enter Keyserver URL"</string> <string name="pref_keyserver">"Search Keyserver"</string> <string name="pref_keyserver_summary">"Search HKP keyserver"</string> @@ -353,6 +356,8 @@ <string name="progress_con_saving">"consolidate: saving to cache…"</string> <string name="progress_con_reimport">"consolidate: reimporting…"</string> + <string name="progress_verifying_keyserver_url">"verifying keyserver…"</string> + <!-- action strings --> <string name="hint_cloud_search_hint">"Search via Name, Email…"</string> @@ -404,6 +409,9 @@ <string name="import_qr_code_button">"Scan QR Code"</string> <string name="import_qr_code_text">"Place your camera over the QR Code!"</string> + <!-- Import from URL --> + <string name="import_url_warn_no_search_parameter">"No search parameter found. You may still attempt manually searching the keyserver."</string> + <!-- Generic result toast --> <string name="view_log">"Details"</string> <string name="with_warnings">", with warnings"</string> @@ -643,6 +651,13 @@ <string name="view_key_fragment_no_system_contact">"<none>"</string> + <!-- Add keyserver --> + <string name="add_keyserver_dialog_title">"Add Keyserver"</string> + <string name="add_keyserver_verified">"Keyserver verified!"</string> + <string name="add_keyserver_without_verification">"Keyserver added without verification."</string> + <string name="add_keyserver_invalid_url">"Invalid URL!"</string> + <string name="add_keyserver_connection_failed">"Failed to connect to keyserver. Please check the URL and your internet connection."</string> + <!-- Navigation Drawer --> <string name="nav_keys">"Keys"</string> <string name="nav_encrypt_decrypt">"Encrypt/Decrypt"</string> @@ -1224,6 +1239,7 @@ <string name="swipe_to_update">"Swipe down to update from keyserver"</string> <string name="error_no_file_selected">"Select at least one file to encrypt!"</string> <string name="error_multi_not_supported">"Saving of multiple files not supported. This is a limitation on current Android."</string> + <string name="error_empty_text">"Type some text to encrypt!"</string> <string name="key_colon">"Key:"</string> <string name="exchange_description">"To start a key exchange, choose the number of participants on the right side, then hit the “Start exchange” button.\n\nYou will be asked two more questions to make sure only the right participants are in the exchange and their fingerprints are correct."</string> <string name="btn_start_exchange">"Start exchange"</string> @@ -16,7 +16,7 @@ Translations are managed at Transifex, please contribute there at https://www.tr ### Contribute Code 1. Join the development mailinglist at http://groups.google.com/d/forum/openpgp-keychain-dev -2. Lookout for interesting issues on our issue page at Github: https://github.com/open-keychain/open-keychain/issues +2. Lookout for interesting issues on Github. We have tagged issues were we explicitly like to see contributions: https://github.com/open-keychain/open-keychain/labels/help-wanted 3. Tell us about your plans on the mailinglist 4. Read this README, especially the notes about coding style 5. Fork OpenKeychain and contribute code (the best part ;) ) diff --git a/build.gradle b/build.gradle index 0b06e229b..baa1f8d97 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ allprojects { maven { // for zxing-android-embedded lib - url "http://dl.bintray.com/journeyapps/maven" + url "https://dl.bintray.com/journeyapps/maven" } } } diff --git a/settings.gradle b/settings.gradle index 16be28694..19f651b89 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,14 +1,12 @@ include ':OpenKeychain' include ':extern:openpgp-api-lib' include ':extern:openkeychain-api-lib' -include ':extern:StickyListHeaders:library' include ':extern:spongycastle:core' include ':extern:spongycastle:pg' include ':extern:spongycastle:pkix' include ':extern:spongycastle:prov' include ':extern:minidns' include ':extern:KeybaseLib:Lib' -include ':extern:TokenAutoComplete:library' include ':extern:safeslinger-exchange' include ':extern:snackbar:lib' include ':OpenKeychain-Test' |